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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 06:05:24  来源:igfitidea点击:

Shell - Pipe to multiple commands in a file

bashshelltee

提问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 stdinis assigned to the reading half of a pipe. bash leaves the name of this pipe on the command line, so that teewill pump its input into the pipe. The subshell's stdoutand stderrare left unchanged, so they are still the same as tee's.

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

So, when you execute

所以,当你执行

tee >(some_command) | some_other_command

Now, bash first creates a process to run tee, and assigns its stdoutto the writing half of a pipe, and another process to run some_other_command, with its stdinassigned to the reading half of the same pipe. Then it creates another process to run some_command, as above, assigning its stdinto the reading half of another pipe, and leaving its stdoutand stderrunchanged. However, stdouthas already been redirected to some_other_command, and that's what some_commandinherits.

现在,bash 首先创建一个进程 run tee,并将其分配给stdout管道的写入部分,另一个进程要 run some_other_command,并将其stdin分配给同一管道的读取部分。然后,它创建另一个进程来运行some_command,如上,其分配stdin到另一个管道的读一半,剩下的stdoutstderr不变。但是,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/5refers 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'