如何逐行将stdin和stdout一起压缩

时间:2018-09-21 05:52:37

标签: bash shell

我有一个简单的命令(my_cc),用于计算每行中的字符数。

此命令分别为text文件生成5、6、7和8。

$ cat text
12345
123456
1234567
12345678

$ cat text | ./my_cc 
5
6
7
8

我的问题是如何逐行将stdin和stdout压缩在一起(没有多个进程):

$ cat text | some_magic_command with my_cc
12345 5
123456 6
1234567 7
12345678 8

可能的答案是:

$ cat text | xargs -I {} bash -c "echo {} | ./my_cc | sed 's/^/{} /g'"
12345 5
123456 6
1234567 7
12345678 8

但这会调用my_cc的进程作为text中的行数。

我无法使用此命令,因为my_cc太重而无法为每一行运行。另外,我无法修改my_cc

2 个答案:

答案 0 :(得分:3)

您可以使用paste

paste -d ' ' text <(./my_cc < text)

这会在text的每一行和命令输出之间放置一个空格。

如果您的外壳不支持进程替换,则可以从标准输入中读取:

./my_cc < text | paste -d ' ' text -

答案 1 :(得分:1)

如果

  1. my_cc不会缓冲其输出,但是会在接收到每行输入后立即写一行输出(大多数命令都不会这样做),并且
  2. 您的文字不是来自文件,而是即时从另一个命令生成的

您可以执行以下操作:

my_cc() {
    perl -nle 'BEGIN { $| = 1 } print length'
}

coproc my_cc
while read -r; do
    printf '%s ' "$REPLY"
    printf '%s\n' "$REPLY" >&${COPROC[1]}
    read -r <&${COPROC[0]}
    printf '%s\n' "$REPLY"
done < <( echo '12345
123456
  .  
1234567
12345678' )

exec {COPROC[0]}<&- {COPROC[1]}>&-
wait $COPROC_PID

输出:

12345 5
123456 6
  .   5
1234567 7
12345678 8

注意:

条件#1是必不可少的。如果my_cc缓冲其输出,则此代码将死锁。

条件2并非严格要求。您可以轻松地在文件(while read -r; do ... done < sometextfile上运行此代码,但是可以多次读取文件,因此可以使用更简单的解决方案(不需要条件#1)。

说明:

  • my_cc被定义为代表您的实际命令的Shell函数。它执行了您描述的操作(打印每行的长度),但是$| = 1值得注释:此语句在当前选定的输出句柄(默认为stdout)上启用自动刷新模式,即,在每个{{ 1}}命令。

  • print是bash内置命令,可在后台(作为协同过程)运行指定的命令。

  • coproc循环从另一个命令(此处由while read -r播放)逐行读取输入。

  • 读取的每一行(echo '...')首先打印,后跟一个空格,然后发送到协同处理。

  • 然后,我们从协同处理中读取一行输出,并打印后跟换行符。

  • 最后,我们关闭协同进程的文件描述符,然后等待其终止。