【问题标题】:Shell - Pipe to multiple commands in a fileShell - 管道到文件中的多个命令
【发布时间】:2013-08-01 18:27:51
【问题描述】:

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

每个命令都是 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,但我不知道如何在脚本中使用命名管道。

【问题讨论】:

  • 只用 sed 做这件事是作弊吗? sed -n 'h;s/World/Boy/g;/Boy/p;x;/World/p' < input.log

标签: bash shell tee


【解决方案1】:

当你执行时

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,并将其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,最初是从stdout (5&gt;&amp;1) 复制的。在 subshel​​l 中,/dev/fd/5 指的是那个 fd。在 subshel​​l 中,可以重定向 stdout,但这会在 stdout 复制到 fd5 后发生。

【讨论】:

  • 这是有道理的,只是我对文件描述符不太感兴趣。知道为什么我会得到:tee:/dev/fd/5:没有这样的文件或目录
  • @SANDeveloper:您需要完全按照所示进行操作。 (tee /dev/fd/5 ... ) 5&gt;&amp;1。这里( ) 5&gt;&amp;1 创建了一个子shell,其中fd5 是fd1 的副本。在该子shell 中,您可以使用/dev/fd/5。与 /dev/fd/4 类似,使用 (...) 4&gt;&amp;1 生成
【解决方案2】:

您可以使用pee(1)——在 Debian/Ubuntu 中,它位于包 moreutils 中。

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

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

【讨论】:

    猜你喜欢
    • 2013-07-07
    • 1970-01-01
    • 2019-07-28
    • 2014-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多