Подтвердить что ты не робот

OS X/Linux: труба в два процесса?

Я знаю о

program1 | program2

и

program1 | tee outputfile | program2

но есть ли способ передать выход program1 как в program2, так и в program3?

4b9b3361

Ответ 1

Вы можете сделать это с помощью tee и заменить процесс.

program1 | tee >(program2) >(program3)

Вывод program1 будет передан на все, что находится внутри ( ), в этом случае program2 и program3.

Ответ 2

Введение о параллелизации

Это кажется тривиальным, но делать это не только возможно, но и это приведет к одновременному или одновременному процессу.

Возможно, вам придется позаботиться о некоторых конкретных эффектах, таких как порядок выполнения, время выхода и т.д.

В конце этого сообщения есть несколько примеров.

Совместимый ответ сначала

По мере того, как этот вопрос помечен и , я сначала поставлю 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`

Примечание. указание полного пути к командам не позволяет интерпретатору оболочки (например, busybox) запускать встроенный компрессор. И делать путь обеспечит тот же синтаксис, который будет выполняться независимо от установки os (пути могут быть разными между MacOS, Ubuntu, RedHat, HP-Ux и т.д.).

Синтаксис NN>&1 (где NN - число от 3 до 63) создает неназванный канал, который мог бы найти в /dev/fd/NN. (Дескрипторы файлов с 0 по 2 уже открыты для 0: STDIN, 1: STDOUT и 2: STDERR).

Попробуйте это (проверено в dash, и ):

(((( 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

или более читаемым:

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  -

Как 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

но вы должны иметь возможность сравнить контрольные суммы 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

Использование функции

Используя некоторые 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  -

будет работать так же.

Окончательная проверка

Это не создаст никакого файла, но позволит сравнить размер сжатого диапазона отсортированных целых чисел, между 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

Это демонстрирует, как использовать stdin и stdout, перенаправленные в подоболочку и, объединенные в консоли для окончательного вывода.

Синтаксис >(...) и <(...)

Последние версии разрешают новый синтаксиса.

seq 1 100000 | wc -l
100000

seq 1 100000 > >( wc -l )
100000

wc -l < <( seq 1 100000 )
100000

Поскольку | является неназванным трудом до /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

Более сложное демо

Для этого требуется утилита 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

Это позволит вам сделать то же предыдущее, следуя синтаксису:

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

Примечание Я использовал другой способ, используемый для вычисления 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

который может отображать:

     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

с этим:

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

может дать:

     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

где порядок зависит от типа, используемого каждой вилкой.

Ответ 3

Вы всегда можете попытаться сохранить вывод программы1 в файл, а затем подать его на входы program2 и program3.

program1 > temp; program2 < temp; program3 < temp;

Ответ 4

В руководстве bash упоминается, как он эмулирует синтаксис >(...), используя либо именованные каналы, либо именованные файловые дескрипторы, поэтому, если вы не хотите зависеть от bash, возможно, вы можете сделать это вручную в своем script.

mknod FIFO
program3 < FIFO &
program1 | tee FIFO | program2
wait
rm FIFO

Ответ 5

Другие ответы вводят понятие. Вот настоящая демонстрация:

$ echo "Leeroy Jenkins" | tee >(md5sum > out1) >(sha1sum > out2) > out3

$ cat out1
11e001d91e4badcff8fe22aea05a7458  -

$ echo "Leeroy Jenkins" | md5sum
11e001d91e4badcff8fe22aea05a7458  -

$ cat out2
5ed25619ce04b421fab94f57438d6502c66851c1  -

$ echo "Leeroy Jenkins" | sha1sum
5ed25619ce04b421fab94f57438d6502c66851c1  -

$ cat out3
Leeroy Jenkins

Конечно, вы можете > /dev/null вместо out3.

Ответ 6

использовать синтаксис (;)... try ps aux | (head -n 1; tail -n 1)