Shell - 管道到文件中的多个命令

时间:2013-08-01 01:26:09

标签: bash shell tee

我想在管道输入上运行2个命令,并希望打印(到stdout)两者的输出。

每个命令都是grep,sed和awk的组合。

这两个命令都必须位于单个.sh文件中。

示例命令:

cat mult_comm.sh       
sed 's/World/Boy/g'|grep Boy ; grep World

# Input
cat input.log
Hello World

# This command HAS to work EXACTLY like this
cat input.log | bash mult_comm.sh

预期输出

Hello Boy
Hello World

实际输出

Hello Boy

我尝试使用tee

cat mult_comm.sh
tee >(sed 's/World/Boy/g'|grep Boy) | grep World

但这只给出了

Hello World

我可以根据需要修改.sh文件,但无法更改管道命令。有什么想法吗?

这类似于OS X / Linux: pipe into two processes?Pipe output to two different commands,但我无法弄清楚如何在脚本中使用命名管道。

2 个答案:

答案 0 :(得分:13)

执行时

tee >(some_command)

bash创建一个子shell来运行some_command。子shell的stdin被分配给管道的读数的一半。 bash在命令行中保留此管道的名称,以便tee将其输入泵入管道。子shell的stdoutstderr保持不变,因此它们仍然与tee相同。

所以,当你执行

tee >(some_command) | some_other_command

现在,bash首先创建一个运行tee的进程,并将其stdout分配给管道的写入一半,另一个进程运行some_other_command,其{{1}分配给同一管道的读数的一半。然后,它创建另一个运行stdin的流程,如上所述,将其some_command分配给另一个管道的读取一半,并保持其stdinstdout不变。但是,stderr已经重定向到stdout,这就是some_other_command继承的内容。

在您的实际示例中,

some_command

我们最终得到:

tee >(sed 's/World/Boy/g'|grep Boy) | grep World

在OP中关联的一个问题中,F. Hauri有一个(未接受但正确的)答案,我在此处进行了调整:

                  -->  sed 's/World/Boy/g' -->  grep Boy --
                 /                                         \
input --> tee --<                                           \
                 \                                           \
                  ----------------------------------------------> grep World 

像上面这样阅读bashisms需要一些练习。重要的是

echo Hello World |
((tee /dev/fd/5 | grep World >/dev/fd/4) \
           5>&1 | sed 's/World/Boy/' | grep Boy) 4>&1

创建一个子shell(( commands ) 5>&1 )并为该子shell提供一个编号为5的fd,最初从( )stdout)复制。在子shell中,5>&1指的是fd。在子shell中,可以重定向/dev/fd/5,但是在将stdout复制到fd5之后会发生这种情况。

答案 1 :(得分:1)

您可以使用pee(1) - 在Debian / Ubuntu中,它可以在moreutils包中找到。

您的示例的用法,比魔术重定向更具可读性

echo Hello World | pee 'grep World' 'sed "s/World/Boy/" | grep Boy'