Bash 只计算输出的前X行的最快方法

Bash 只计算输出的前X行的最快方法,bash,wc,Bash,Wc,我有一个来自tshark过滤器的大型终端输出,我想检查行数(本例中的包数)是否达到X的阈值 这个操作是在许多大文件的循环中完成的,所以我想在这里最大限度地提高性能 我想知道的是,wc-l是计算终端命令输出的最快方法 我的行如下所示:(所以tshark命令在这里并不重要,所以为了可读性,我替换了它) 虽然这几乎可以正常工作,但我想知道是否有办法在阈值之后停止。只要达到(或未达到)阈值,准确的数字就无关紧要 一个猜测是: HEAD=$((THRESHOLD+1)) [[ $(tshark -r $f

我有一个来自
tshark
过滤器的大型终端输出,我想检查行数(本例中的包数)是否达到X的阈值

这个操作是在许多大文件的循环中完成的,所以我想在这里最大限度地提高性能

我想知道的是,
wc-l
是计算终端命令输出的最快方法

我的行如下所示:(所以tshark命令在这里并不重要,所以为了可读性,我替换了它)

虽然这几乎可以正常工作,但我想知道是否有办法在阈值之后停止。只要达到(或未达到)阈值,准确的数字就无关紧要

一个猜测是:

HEAD=$((THRESHOLD+1))
[[ $(tshark -r $file -Y "tcp.stream==${streamID}" | head -n $HEAD | wc -l) -gt $THRESHOLD ]] || echo "not enough"
但是,通过管道连接到其他服务并增加阈值可能会更慢,不是吗


编辑:将示例代码更改为工作的tshark片段

使用包装器启动
tshark
(或
tail-f-n+1文件
),该包装器检查输出行计数并在阈值后退出。下面是一个在awk中使用
seq
模拟
tshark
的示例:

$ awk '
BEGIN {
    cmd="seq 1 100"                        # command to execute, outputs 100 lines
    while((cmd|getline res)>0 && ++c<50);  # count to 50 lines and exit
    print res                              # test to show last line of input
    exit
}'
seq
在50岁之后继续运行一段时间,但最终退出。更改
cmd=“seq 1 10000000 | tee foo”
tail foo
我得到:

...
11407
11408
11
基准 只有一种方法可以找到答案:自己进行基准测试。 下面是我想到的一些实现

gen() { seq "$max"; }
# functions returning 0 (success) iff `gen` prints less than `$thold` lines
a() { [ "$(gen | head -n"$thold" | wc -l)" != "$thold" ]; }
b() { [ -z "$(gen | tail -n+"$thold" | head -c1)" ]; }
c() { [ "$(gen | grep -cm"$thold" ^)" != "$thold" ]; }
d() { [ "$(gen | grep -Fcm"$thold" '')" != "$thold" ]; }
e() { gen | awk "NR >= $thold{exit 1}"; }
f() { gen | awk -F^ "NR >= $thold{exit 1}"; }
g() { gen | sed -n "$thold"q1; }
h() { mapfile -n1 -s"$thold" < <(gen); [ -z "$MAPFILE" ]; }

max=1''000''000''000
for fn in {a..h}; do
  printf '%s: ' "$fn"
  for ((thold=1''000''000; thold<=max; thold*=10)); do
    printf '%.0e=%2.1fs, ' "$thold" "$({ time -p "$fn"; } 2>&1 | grep -Eom1 '[0-9.]+')"
  done
  echo
done
结果 以下是我的系统上的结果:

a: 1e+06=0.0s, 1e+07=0.1s, 1e+08=0.8s, 1e+09=8.9s,
b: 1e+06=0.0s, 1e+07=0.1s, 1e+08=0.9s, 1e+09=8.4s,
c: 1e+06=0.0s, 1e+07=0.2s, 1e+08=1.6s, 1e+09=16.1s,
d: 1e+06=0.0s, 1e+07=0.2s, 1e+08=1.6s, 1e+09=15.7s,
e: 1e+06=0.1s, 1e+07=0.8s, 1e+08=8.2s, 1e+09=83.2s,
f: 1e+06=0.1s, 1e+07=0.8s, 1e+08=8.2s, 1e+09=84.6s,
g: 1e+06=0.0s, 1e+07=0.3s, 1e+08=3.0s, 1e+09=31.6s,
h: 1e+06=7.7s, 1e+07=90.0s, ... (manually aborted)
b:。。。1e+08=0.9s…
意味着接近
b
花费了0.9秒来发现
seq 100000000
的输出至少有
1e+08
(=100000)行

结论 从这个答案中给出的方法来看,
b
显然是最快的。但是,实际结果可能因系统而异(对于
head
grep
,…)和您的atual用例,有不同的实现和版本。我建议使用实际数据进行基准测试(即,将
gen()
中的
seq
替换为
t共享输出行
,并将
thold
设置为任何实际使用的值)


如果您需要更快的方法,您可以使用
stdbuf
LC_ALL=C

进行更多的实验,您是否确实尝试过计时?我想我无法模拟你的平台你的想法是正确的。避免产生额外的进程。你的问题是小鸡或鸡蛋的问题。在进行有效比较之前,您必须至少阅读
$THRESHOLD
行(或
EOF
)。由于您是管道
t共享输出
,因此该过程将在传递到
wc
head
之前完成。除非
$THRESHOLD
和文件大小之间有几十万行+差异,否则我不知道您在
wc
head
之间节省了任何时间。你只需要对最坏的情况进行计时就可以了。在我的场景中,很难隔离这一步(需要更多的编码),所以我的想法是在开始工作之前先考虑一下,最终什么都不做。感谢David的回答……您可以将
tsharks
的输出通过管道传输到一个程序中,该程序不仅可以验证它(如您所述),还可以关闭它的stdin。写入程序(
tsharks
)应该在管道破裂的情况下中止。@user1934428
head
已经关闭stdin。令人惊讶的是
b
对我来说也是最快的。@KamilCuk感谢您分享您的结果。你为什么会感到惊讶?我想我希望只有一个进程,比如
awk
获胜,而不是两个进程
tail | head
。在我的系统上,这也是方法b)。感谢这个很好的基准测试示例!我本以为sed会在这里获胜<代码>头部和尾部确实很快。。。但没那么快。我相信样本数据也是一个玩家。如果您将
gen
替换为
printf--“x%.0s”$(seq$(max*70))| fold-w70
printf--“x%.0s”$(seq$(max*6000))| fold-w6000
(后者被选择为大于
PIPE\BUF
gen() { seq "$max"; }
# functions returning 0 (success) iff `gen` prints less than `$thold` lines
a() { [ "$(gen | head -n"$thold" | wc -l)" != "$thold" ]; }
b() { [ -z "$(gen | tail -n+"$thold" | head -c1)" ]; }
c() { [ "$(gen | grep -cm"$thold" ^)" != "$thold" ]; }
d() { [ "$(gen | grep -Fcm"$thold" '')" != "$thold" ]; }
e() { gen | awk "NR >= $thold{exit 1}"; }
f() { gen | awk -F^ "NR >= $thold{exit 1}"; }
g() { gen | sed -n "$thold"q1; }
h() { mapfile -n1 -s"$thold" < <(gen); [ -z "$MAPFILE" ]; }

max=1''000''000''000
for fn in {a..h}; do
  printf '%s: ' "$fn"
  for ((thold=1''000''000; thold<=max; thold*=10)); do
    printf '%.0e=%2.1fs, ' "$thold" "$({ time -p "$fn"; } 2>&1 | grep -Eom1 '[0-9.]+')"
  done
  echo
done
a && echo "tsharks printed less than $thold lines"
a: 1e+06=0.0s, 1e+07=0.1s, 1e+08=0.8s, 1e+09=8.9s,
b: 1e+06=0.0s, 1e+07=0.1s, 1e+08=0.9s, 1e+09=8.4s,
c: 1e+06=0.0s, 1e+07=0.2s, 1e+08=1.6s, 1e+09=16.1s,
d: 1e+06=0.0s, 1e+07=0.2s, 1e+08=1.6s, 1e+09=15.7s,
e: 1e+06=0.1s, 1e+07=0.8s, 1e+08=8.2s, 1e+09=83.2s,
f: 1e+06=0.1s, 1e+07=0.8s, 1e+08=8.2s, 1e+09=84.6s,
g: 1e+06=0.0s, 1e+07=0.3s, 1e+08=3.0s, 1e+09=31.6s,
h: 1e+06=7.7s, 1e+07=90.0s, ... (manually aborted)