OS X/Linux:管道分为两个进程?

Jas*_*n S 21 unix macos shell pipe

我知道

program1 | program2
Run Code Online (Sandbox Code Playgroud)

program1 | tee outputfile | program2
Run Code Online (Sandbox Code Playgroud)

但有没有办法将program1的输出提供给program2和program3?

ins*_*r-g 28

您可以使用tee和处理替换.

program1 | tee >(program2) >(program3)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,输出program1将通过管道输送到内部的任何内容.( )program2program3

  • 当`tee`复制到每个*文件***以及标准输出**,并且``()`将被`tee`看作*文件*时,你必须写:`prg1 | tee>(prg2)>>(prg3)`****不要**在控制台上转储`prg1`的ouptut(注意补充`>`). (12认同)
  • @StephenNiedzielski关心:在这种情况下,`prg3`将读为*stdin*,`prg2`的输出连接到`prg1`的输出! (2认同)

F. *_*uri 20

关于并行化的介绍

这似乎微不足道,但这样做不仅是可能的,同时也会产生并发同时的过程.

您可能需要注意某些特定效果,例如执行顺序,执行时间等.

这篇文章末尾有一些样本.

首先是兼容的答案

由于这个问题被标记为 ,我将首先给出POSIX兼容的答案.(对于讽刺,进一步说.)

是的,有一种方法可以使用未命名的管道.

在这个样本中,我将生成一个100'000的数字范围,随机化它们并使用4种不同的压缩工具压缩结果来比较压缩比...

为此,我将首先进行准备:

GZIP_CMD=`which gzip`
BZIP2_CMD=`which bzip2`
LZMA_CMD=`which lzma`
XZ_CMD=`which xz`
MD5SUM_CMD=`which md5sum`
SED_CMD=`which sed`
Run Code Online (Sandbox Code Playgroud)

注意:指定命令的完整路径会阻止某些shell解释器(如busybox)运行内置压缩器.并且方法将确保相同的语法将独立于os安装运行(MacO,Ubuntu,RedHat,HP-Ux之间的路径可能不同......).

语法NN>&1(其中NN是介于3和63之间的数字)确实会生成未命名的管道,可以找到/dev/fd/NN.(文件描述符0到2已经为0打开:STDIN,1:STDOUT和2:STDERR).

试试这个(在,下测试):

(((( seq 1 100000 | shuf | tee /dev/fd/4 /dev/fd/5 /dev/fd/6 /dev/fd/7 | $GZIP_CMD >/tmp/tst.gz ) 4>&1 | $BZIP2_CMD >/tmp/tst.bz2 ) 5>&1 | $LZMA_CMD >/tmp/tst.lzma ) 6>&1 | $XZ_CMD >/tmp/tst.xz ) 7>&1 | $MD5SUM_CMD
Run Code Online (Sandbox Code Playgroud)

或更具可读性:

GZIP_CMD=`which gzip`
BZIP2_CMD=`which bzip2`
LZMA_CMD=`which lzma`
XZ_CMD=`which xz`
MD5SUM_CMD=`which md5sum`

(
  (
    (
      (
        seq 1 100000 |
          shuf |
          tee /dev/fd/4 /dev/fd/5 /dev/fd/6 /dev/fd/7 |
          $GZIP_CMD >/tmp/tst.gz
      ) 4>&1 |
        $BZIP2_CMD >/tmp/tst.bz2
    ) 5>&1 |
      $LZMA_CMD >/tmp/tst.lzma
  ) 6>&1 |
    $XZ_CMD >/tmp/tst.xz
) 7>&1 |
  $MD5SUM_CMD
2e67f6ad33745dc5134767f0954cbdd6  -
Run Code Online (Sandbox Code Playgroud)

shuf随机放置一样,如果你试试这个,你必须获得不同的结果,

ls -ltrS /tmp/tst.*
-rw-r--r-- 1 user user 230516 oct  1 22:14 /tmp/tst.bz2
-rw-r--r-- 1 user user 254811 oct  1 22:14 /tmp/tst.lzma
-rw-r--r-- 1 user user 254892 oct  1 22:14 /tmp/tst.xz
-rw-r--r-- 1 user user 275003 oct  1 22:14 /tmp/tst.gz
Run Code Online (Sandbox Code Playgroud)

但你必须能够比较md5校验和:

SED_CMD=`which sed`

for chk in gz:$GZIP_CMD bz2:$BZIP2_CMD lzma:$LZMA_CMD xz:$XZ_CMD;do
    ${chk#*:} -d < /tmp/tst.${chk%:*} |
        $MD5SUM_CMD |
        $SED_CMD s/-$/tst.${chk%:*}/
  done
2e67f6ad33745dc5134767f0954cbdd6  tst.gz
2e67f6ad33745dc5134767f0954cbdd6  tst.bz2
2e67f6ad33745dc5134767f0954cbdd6  tst.lzma
2e67f6ad33745dc5134767f0954cbdd6  tst.xz
Run Code Online (Sandbox Code Playgroud)

使用功能

使用一些bashims,这可能看起来更好,供样品使用/dev/fd/{4,5,6,7},而不是tee /dev/fd/4 /dev/fd/5 /...

(((( seq 1 100000 | shuf | tee /dev/fd/{4,5,6,7} | gzip >/tmp/tst.gz ) 4>&1 |
   bzip2 >/tmp/tst.bz2 ) 5>&1 | lzma >/tmp/tst.lzma ) 6>&1 |
   xz >/tmp/tst.xz ) 7>&1 | md5sum
29078875555e113b31bd1ae876937d4b  -
Run Code Online (Sandbox Code Playgroud)

会一样的.

最后检查

这不会创建任何文件,但可以让你比较一个压缩范围的排序整数的大小,在4种不同的压缩工具之间(为了好玩,我用4种不同的格式输出方式):

(
  (
    (
      (
        (
          seq 1 100000 |
            tee /dev/fd/{4,5,6,7} |
              gzip |
              wc -c |
              sed s/^/gzip:\ \ / >&3
        ) 4>&1 |
          bzip2 |
          wc -c |
          xargs printf "bzip2: %s\n" >&3
      ) 5>&1 |
        lzma |
        wc -c |
        perl -pe 's/^/lzma:   /' >&3
    ) 6>&1 |
      xz |
      wc -c |
      awk '{printf "xz: %9s\n",$1}' >&3
  ) 7>&1 |
    wc -c
) 3>&1
gzip:  215157
bzip2: 124009
lzma:   17948
xz:     17992
588895
Run Code Online (Sandbox Code Playgroud)

这演示了如何使用子shell中重定向的stdinstdout ,并在控制台中合并以进行最终输出.

语法>(...)<(...)

最近的版本允许新的语法功能.

seq 1 100000 | wc -l
100000

seq 1 100000 > >( wc -l )
100000

wc -l < <( seq 1 100000 )
100000
Run Code Online (Sandbox Code Playgroud)

由于|是一个未命名的管道/dev/fd/0,语法<()不生成临时无名管道与其他文件描述符/dev/fd/XX.

md5sum <(zcat /tmp/tst.gz) <(bzcat /tmp/tst.bz2) <(
         lzcat /tmp/tst.lzma) <(xzcat /tmp/tst.xz)
29078875555e113b31bd1ae876937d4b  /dev/fd/63
29078875555e113b31bd1ae876937d4b  /dev/fd/62
29078875555e113b31bd1ae876937d4b  /dev/fd/61
29078875555e113b31bd1ae876937d4b  /dev/fd/60
Run Code Online (Sandbox Code Playgroud)

更复杂的演示

这需要安装GNU file实用程序.将确定由扩展名或文件类型运行的命令.

for file in /tmp/tst.*;do
    cmd=$(which ${file##*.}) || {
        cmd=$(file -b --mime-type $file)
        cmd=$(which ${cmd#*-})
    }
    read -a md5 < <($cmd -d <$file|md5sum)
    echo $md5 \ $file
  done
29078875555e113b31bd1ae876937d4b  /tmp/tst.bz2
29078875555e113b31bd1ae876937d4b  /tmp/tst.gz
29078875555e113b31bd1ae876937d4b  /tmp/tst.lzma
29078875555e113b31bd1ae876937d4b  /tmp/tst.xz
Run Code Online (Sandbox Code Playgroud)

这可以通过以下语法执行相同的操作:

seq 1 100000 |
    shuf |
        tee >(
            echo gzip. $( gzip | wc -c )
          )  >(
            echo gzip, $( wc -c < <(gzip))
          ) >(
            gzip  | wc -c | sed s/^/gzip:\ \ /
          ) >(
            bzip2 | wc -c | xargs printf "bzip2: %s\n"
          ) >(
            lzma  | wc -c | perl -pe 's/^/lzma:  /'
          ) >(
            xz    | wc -c | awk '{printf "xz: %9s\n",$1}'
          ) > >(
            echo raw: $(wc -c)
          ) |
        xargs printf "%-8s %9d\n"

raw:        588895
xz:         254556
lzma:       254472
bzip2:      231111
gzip:       274867
gzip,       274867
gzip.       274867
Run Code Online (Sandbox Code Playgroud)

注意我使用了不同的方式来计算gzip压缩计数.

注意由于此操作是同时完成的,因此输出顺序将取决于每个命令所需的时间.

进一步了解并行化

如果您运行某些多核或多处理器计算机,请尝试比较:

i=1
time for file in /tmp/tst.*;do
    cmd=$(which ${file##*.}) || {
        cmd=$(file -b --mime-type $file)
        cmd=$(which ${cmd#*-})
    }
    read -a md5 < <($cmd -d <$file|md5sum)
    echo $((i++)) $md5 \ $file
  done |
cat -n
Run Code Online (Sandbox Code Playgroud)

可能会呈现:

     1      1 29078875555e113b31bd1ae876937d4b  /tmp/tst.bz2
     2      2 29078875555e113b31bd1ae876937d4b  /tmp/tst.gz
     3      3 29078875555e113b31bd1ae876937d4b  /tmp/tst.lzma
     4      4 29078875555e113b31bd1ae876937d4b  /tmp/tst.xz

real    0m0.101s
Run Code Online (Sandbox Code Playgroud)

有了这个:

time  (
    i=1 pids=()
    for file in /tmp/tst.*;do
        cmd=$(which ${file##*.}) || {
            cmd=$(file -b --mime-type $file)
            cmd=$(which ${cmd#*-})
        }
        (
             read -a md5 < <($cmd -d <$file|md5sum)
             echo $i $md5 \ $file
        ) & pids+=($!)
      ((i++))
      done
    wait ${pids[@]}
) |
cat -n
Run Code Online (Sandbox Code Playgroud)

可以给:

     1      2 29078875555e113b31bd1ae876937d4b  /tmp/tst.gz
     2      1 29078875555e113b31bd1ae876937d4b  /tmp/tst.bz2
     3      4 29078875555e113b31bd1ae876937d4b  /tmp/tst.xz
     4      3 29078875555e113b31bd1ae876937d4b  /tmp/tst.lzma

real    0m0.070s
Run Code Online (Sandbox Code Playgroud)

排序取决于每个fork使用的类型.

  • 这太棒了!做得太好了 :) (2认同)

Pet*_*tar 1

您始终可以尝试将program1的输出保存到文件中,然后将其输入program2和program3输入。

program1 > temp; program2 < temp; program3 < temp;
Run Code Online (Sandbox Code Playgroud)

  • 是的,我知道(或 `program1 | tee outputfile | program2; program3 &lt;outputfile`),但我想避免这样做,如果这样做很容易的话。 (6认同)