我有一个简单的命令(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
。
答案 0 :(得分:3)
您可以使用paste
:
paste -d ' ' text <(./my_cc < text)
这会在text
的每一行和命令输出之间放置一个空格。
如果您的外壳不支持进程替换,则可以从标准输入中读取:
./my_cc < text | paste -d ' ' text -
答案 1 :(得分:1)
如果
my_cc
不会缓冲其输出,但是会在接收到每行输入后立即写一行输出(大多数命令都不会这样做),并且您可以执行以下操作:
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 '...'
)首先打印,后跟一个空格,然后发送到协同处理。
然后,我们从协同处理中读取一行输出,并打印后跟换行符。
最后,我们关闭协同进程的文件描述符,然后等待其终止。