从管道

时间:2017-08-09 13:51:05

标签: bash

如何从一个从管道中读取的程序获得持续更新的输出?例如,我们假设此程序是wc的一个版本:

$ ls | running_wc

所以我想要立即输出,例如

0 0 0

然后每次收到新的输出行时,它都会再次更新,例如

1 2 12
2 4 24
etc.

当然我的命令并不是ls,它是一个慢慢输出数据的过程......我真的喜欢动态地让它计算匹配和非匹配,以及将此信息汇总到一行,例如

$ my_process | count_matches error

这将不断更新具有匹配和非匹配计数的单行输出,例如:

$ my_process | count_matches error
0 5

然后它可能看起来像是这样,因为它找到了2个匹配和10个不匹配的行。

$ my_process | count_matches error
2 10

2 个答案:

答案 0 :(得分:3)

dd会在收到SIGUSR1信号后打印出统计信息,但wcgrep都不会。你需要或多或少地重新实现它们。

count_matches() {
    local pattern=$1
    local matches=0 nonmatches=0
    local line

    while IFS= read -r line; do
        if [[ $line == *$pattern* ]]; then ((++matches)); else ((++nonmatches)); fi
        printf '\r%s %s' "$matches" "$nonmatches"
    done

    printf '\n'
}

每次打印回车\r会导致打印输出相互覆盖。

在管道中使用时,大多数程序将从行缓冲切换到完全缓冲。您的慢速运行程序应在每行后刷新其输出,以确保结果立即可用。或者,如果您无法修改它,您通常可以使用stdbuf -oL强制使用C stdio的程序行缓冲区标准输出。

stdbuf -oL my_process | count_matches error

答案 1 :(得分:1)

使用awk。首先,我们创建“my_process”:

$ for i in {1..10} ; do echo $i ; sleep 1 ; done # slowly prints lines

比赛计数器:

$ awk 'BEGIN {           
    print "match","miss"   # print header
    m=0                    # reset match count
}
{
    if($1~/(3|6)/)         # match is a 3 or 6 (for this output)
        m++                # increment match count
    print m,NR-m           # for each record output match / miss counts
}'

运行它:

$ for i in {1..10} ; do echo $i ; sleep 1 ; done | awk 'BEGIN{print "match","miss";m=0}{if($1~/(3|6)/)m++;print m,NR-m}'
match miss
0 1
0 2
1 2
1 3
1 4
2 4
2 5
2 6
2 7
2 8