bash Shell - 管道到文件中的多个命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17983777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Shell - Pipe to multiple commands in a file
提问by SANDeveloper
I want to run 2 commands on a piped-in input and want to print (to stdout) the output of both.
我想在管道输入上运行 2 个命令,并想打印(到标准输出)两者的输出。
Each command is a combination of grep, sed and awk.
每个命令都是 grep、sed 和 awk 的组合。
Both these commands must reside in a single .sh file.
这两个命令都必须驻留在一个 .sh 文件中。
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
我试过用 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?
我可以根据需要修改 .sh 文件,但无法更改管道命令。有任何想法吗?
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.
这类似于OS X / Linux:管道进入两个进程?和管道输出到两个不同的命令,但我不知道如何在脚本中使用命名管道。
回答by rici
When you execute
当你执行
tee >(some_command)
bash creates a subshell to run some_command
. The subshell's stdin
is assigned to the reading half of a pipe. bash leaves the name of this pipe on the command line, so that tee
will pump its input into the pipe. The subshell's stdout
and stderr
are left unchanged, so they are still the same as tee
's.
bash 创建一个子 shell 来运行some_command
. 子外壳stdin
被分配给管道的读取部分。bash 将这个管道的名称留在命令行上,以便tee
将其输入泵入管道。子外壳的stdout
和stderr
保持不变,因此它们仍然与tee
's相同。
So, when you execute
所以,当你执行
tee >(some_command) | some_other_command
Now, bash first creates a process to run tee
, and assigns its stdout
to the writing half of a pipe, and another process to run some_other_command
, with its stdin
assigned to the reading half of the same pipe. Then it creates another process to run some_command
, as above, assigning its stdin
to the reading half of another pipe, and leaving its stdout
and stderr
unchanged. However, stdout
has already been redirected to some_other_command
, and that's what some_command
inherits.
现在,bash 首先创建一个进程 run tee
,并将其分配给stdout
管道的写入部分,另一个进程要 run some_other_command
,并将其stdin
分配给同一管道的读取部分。然后,它创建另一个进程来运行some_command
,如上,其分配stdin
到另一个管道的读一半,剩下的stdout
和stderr
不变。但是,stdout
已经被重定向到some_other_command
,这就是some_command
继承。
In your actual example,
在您的实际示例中,
tee >(sed 's/World/Boy/g'|grep Boy) | grep World
we end up with:
我们最终得到:
--> sed 's/World/Boy/g' --> grep Boy --
/ \
input --> tee --< \
\ \
----------------------------------------------> grep World
In one of the questions linked in the OP, there is a (non-accepted but correct) answer by F. Hauri, which I've adapted here:
在 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
It takes a little practice to read bashisms like the above. The important part is that
阅读上述的 bashisms 需要一些练习。重要的部分是
( commands ) 5>&1
Creates a subshell (( )
) and gives that subshell an fd numbered 5, initially copied from stdout
(5>&1
). Inside the subshell, /dev/fd/5
refers to that fd. Within the subshell, it is possible to redirect stdout
, but that will happen after stdout is copied to fd5.
创建一个子外壳 ( ( )
) 并为该子外壳提供编号为 5 的 fd,最初是从stdout
( 5>&1
)复制的。在子shell里面,/dev/fd/5
指的是那个fd。在子shell 中,可以重定向stdout
,但这会在将stdout 复制到fd5 之后发生。
回答by Morten
You can use pee(1)
– in Debian/Ubuntu it's available in the package moreutils
.
您可以使用pee(1)
– 在 Debian/Ubuntu 中,它在包中可用moreutils
。
Usage for your example, somewhat more readable than the magic redirection
用于您的示例,比魔术重定向更具可读性
echo Hello World | pee 'grep World' 'sed "s/World/Boy/" | grep Boy'