Intereting Posts
Могу ли я туннелировать интернет с одного компьютера на другой в своей сети? Как найти источник текущего ядра на моей машине? найти всю директорию с тем же именем и удалить все содержимое в centos `docker logs foo | less` не доступен для поиска или прокрутки, но `docker logs foo 2> & 1 | меньше` является LVM: отражается ли это? скопировать это медленно? Я установил CentOS 7 в Virtual Box, но сетевые настройки отсутствуют Как улучшить производительность 2D-графики с помощью Android-x86, работающего на KVM? Рекомендуемый максимальный процент для заполнения большого диска данных ext4 Как установить Adobe Flash на Debian Wheezy? OpenBSD – игнорирование конкретного нажатия клавиши Переустановка / дом из другого раздела при новой установке без копирования Есть ли инструменты для редактирования файла initrd в Windows? Vim: Создание XML-текста «довольно» Сервер RAID-1 / LVM не загружается после отключения питания Разрешить пользователю выполнять одну команду в качестве другого пользователя

тире: Труба STDIN к нескольким командам и их вывод в STDOUT в определенном порядке

Сначала я думал, что этот ответ был решением, но теперь я думаю, что мне нужен временный файл в качестве буфера.

Это работает неслучайно:

#!/bin/sh echo 'OK' | { { tee /dev/fd/3 | head --bytes=1 >&4 } 3>&1 | tail --bytes=+2 >&4 } 4>&1 

Когда я запускаю это в терминале, иногда я получаю:

ОК

и иногда я получаю:

К
О

Кажется совершенно случайным. Поэтому в качестве обходного пути я пишу вывод tail в файл и читаю его обратно в stdout после завершения работы.

 #!/bin/sh echo 'OK' | { { tee /dev/fd/3 | head --bytes=1 >&4 } 3>&1 | tail --bytes=+2 >file } 4>&1 cat file 

Можно ли это сделать в dash без временных файлов? Переменные Shell в качестве буфера также не являются опцией, так как вывод может содержать NUL байты.

Лучшее решение использовать временные файлы. Это делает код понятным и понятным, когда замена процесса не является вариантом.

 tmpfile=$(mktemp) producer | tee "$tmpfile" | consumer1 consumer2 <"$tmpfile" rm -f "$tmpfile" 

или даже

 tmpfile=$(mktemp) producer >"$tmpfile" consumer1 <"$tmpfile" consumer2 <"$tmpfile" rm -f "$tmpfile" 

Если вы хотите параллельно запускать потребителей и производителей, а сериализовать выходные данные потребителей, вам нужно будет отложить вывод второго потребителя. Для этого вам нужно как-то сохранить свой выход, и лучший способ – с временным файлом.

С zsh :

 {cat =(producer > >(consumer1 >&3) | consumer2)} 3>&1 

bash есть проблема в том, что он не ждет команд подстановки процесса, поэтому вам придется использовать неприятную работу там .

Здесь мы используем форму =(...) подстановки процесса для хранения вывода comsumer2 во временном файле и потом его cat . Мы не можем сделать это для более чем 2 потребителей. Для этого нам нужно создать временные файлы вручную.

Если вы не используете =(...) , нам придется вручную очищать временные файлы. Мы можем справиться с этим, создавая и удаляя их спереди, чтобы не беспокоиться о случаях, когда сценарий убит. Все еще с zsh :

 tmp1=$(mktemp) && tmp2=$(mktemp) || exit { rm -f -- $tmp1 $tmp2 producer > >(consumer1) > >(consumer2 >&3) > >(consumer3 >&5) cat <&4 <&6 } 3> $tmp1 4< $tmp1 5> $tmp2 6< $tmp2 

Изменить (я изначально упустил тот факт, что требуется решение для dash )

Для dash (или любой оболочки POSIX, которая не устанавливает флаг close-on-exec на fds выше 2 и использует каналы, а не сокеты для | ), а также в системах с поддержкой /dev/fd/x :

 tmp1=$(mktemp) && tmp2=$(mktemp) || exit { rm -f -- "$tmp1" "$tmp2" { { { producer | tee /dev/fd/4 /dev/fd/6 | consumer1 >&7 } 4>&1 | consumer2 >&3 } 6>&1 | consumer3 >&5 } 7>&1 cat - /dev/fd/6 <&4 } 3> "$tmp1" 4< "$tmp1" 5> "$tmp2" 6< "$tmp2" 

Это будет работать с dash , bash , zsh , mksh , busybox sh , posh на Linux, но не ksh93 . Этот подход не может выходить за пределы 4-х потребителей, так как мы ограничены fds от 0 до 9.