5

I want to run 2 commands on a piped-in input and want to print (to stdout) the output of both.

Each command is a combination of grep, sed and awk.

Both these commands must reside in a single .sh file.

Sample commands:

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

Expected output

Hello Boy
Hello World

Actual output

Hello Boy

I tried using tee

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

But this gives only

Hello World

I can modify the .sh file as I want but the piped command can't be changed. Any ideas?

This is similar to OS X / Linux: pipe into two processes? and Pipe output to two different commands, but I can't figure out how to use named pipes inside the script.

4

2 回答 2

14

当你执行

tee >(some_command)

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

所以,当你执行

tee >(some_command) | some_other_command

现在, bash 首先创建一个进程 run tee,并将其分配给stdout管道的写入部分,然后再创建另一个进程 run some_other_command,并将其stdin分配给同一管道的读取部分。然后它创建另一个进程来运行some_command,如上所述,将其分配给stdin另一个管道的读取部分,并保持其stdoutstderr不变。但是,stdout已经被重定向到some_other_command,这就是some_command继承。

在您的实际示例中,

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

我们最终得到:

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

在 OP 中链接的一个问题中,F. Hauri有一个(不接受但正确的)答案,我在这里对其进行了改编:

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

阅读上述的 bashism 需要一点练习。重要的部分是

( commands ) 5>&1

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

于 2013-08-01T02:08:43.147 回答
1

You can use pee(1) – in Debian/Ubuntu it's available in the package moreutils.

Usage for your example, somewhat more readable than the magic redirection

echo Hello World | pee 'grep World' 'sed "s/World/Boy/" | grep Boy'
于 2013-08-01T10:33:42.750 回答