为不同的命令重用管道数据

arn*_*can 7 shell pipe tee

我想对不同的应用程序使用相同的管道,例如:

cat my_file | {
  cmd1
  cmd2
  cmd3
}
Run Code Online (Sandbox Code Playgroud)

Cmd1 应该消耗部分输入。Cmd2 应该消耗另一部分等等。

但是,每个 cmd 都会吃掉更多的输入,然后它读取的内容确实需要适当的缓冲。

例如:

yes | nl | { 
  head -n 10 > /dev/null
  cat 
} | head -n 10
Run Code Online (Sandbox Code Playgroud)

来自第 912 行而不是第 11 行的输出。

Tee 不是一个好的选择,因为每个命令都应该消耗一部分标准输入。

有没有一种简单的方法可以让这个工作?

F. *_*uri 4

您可以使用tee许多命令来复制命令来处理整个流:

( ( seq 1 10 | tee /dev/fd/5 | sed s/^/line..\ / >&4 ) 5>&1 | wc -l ) 4>&1 
line.. 1
line.. 2
line.. 3
line.. 4
line.. 5
line.. 6
line.. 7
line.. 8
line.. 9
line.. 10
10
Run Code Online (Sandbox Code Playgroud)

或者使用 bash 逐行分割:

while read line ;do
    echo cmd1 $line
    read line && echo cmd2 $line
    read line && echo cmd3 $line
  done < <(seq 1 10)
cmd1 1
cmd2 2
cmd3 3
cmd1 4
cmd2 5
cmd3 6
cmd1 7
cmd2 8
cmd3 9
cmd1 10
Run Code Online (Sandbox Code Playgroud)

最后有一种方法可以运行cmd1cmd2并且cmd3只有一次,1/3 的流作为STDIN

( ( ( seq 1 10 |
         tee /dev/fd/5 /dev/fd/6 |
           sed -ne '1{:a;p;N;N;N;s/^.*\n//;ta;}' |
           cmd1 >&4
     ) 5>&1 |
       sed -ne '2{:a;p;N;N;N;s/^.*\n//;ta;}' |
       cmd2 >&4
  ) 6>&1 |
    sed -ne '3{:a;p;N;N;N;s/^.*\n//;ta;}' |
    cmd3 >&4
) 4>&1 
command_1: 1
command_1: 4
command_1: 7
command_1: 10
Command-2: 2
Command-2: 5
Command-2: 8
command 3: 3
command 3: 6
command 3: 9
Run Code Online (Sandbox Code Playgroud)

为了尝试这个,你可以使用:

alias cmd1='sed -e "s/^/command_1: /"' \
    cmd2='sed -e "s/^/Command_2: /"' \
    cmd3='sed -e "s/^/Command_3: /"'
Run Code Online (Sandbox Code Playgroud)

如果在同一脚本上,要在不同进程上使用一个流,您可以这样做:

(
    for ((i=(RANDOM&7);i--;));do
        read line;
        echo CMD1 $line
      done
    for ((i=RANDOM&7;i--;));do
        read line
        echo CMD2 $line
      done
    while read line ;do
        echo CMD3 $line
      done
)
CMD1 1
CMD1 2
CMD1 3
CMD2 4
CMD2 5
CMD2 6
CMD2 7
CMD2 8
CMD2 9
CMD3 10
Run Code Online (Sandbox Code Playgroud)

为此,您可能必须将单独的脚本转换为bash 函数,以便能够构建一个整体脚本。

另一种方法可能是确保每个脚本不会向STDOUT输出任何内容,而不是cat在每个脚本的末尾添加一个以便能够链接它们:

#!/bin/sh

for ((i=1;1<n;i++));do
   read line
   pRoCeSS the $line
   echo >output_log
 done

cat
Run Code Online (Sandbox Code Playgroud)

最终命令可能如下所示:

seq 1 10 | cmd1 | cmd2 | cmd2
Run Code Online (Sandbox Code Playgroud)