Как я могу отправить stdout в несколько команд?

Если Вы находитесь на современном Linux, Вы могли бы использовать inotifywait, одна из программ командной строки от пакета inotify-инструментов. Я не сделал этого, но это похоже inotifywait мог использоваться в сценарии оболочки для сброса символьной ссылки и остановки и перезапуска tail -f на символьной ссылке. Вы могли бы даже смочь избавиться от символьной ссылки и просто выследить текущий файл журнала.

Не используя inotify, возможно записать Вашу собственную подобную хвосту программу, которая проверяет, чтобы видеть, изменилось ли inode количество имени файла, из которого это читает. В той точке может закрыться подобная хвосту программа, это - старый дескриптор файла, и получите новый дескриптор файла для теперь различного имени файла. Это - хитрая программа для записи, и это подвержено условиям состязания.

197
08.01.2012, 01:57
10 ответов

Можно использовать tee и замена процесса на это:

cat file.txt | tee >(pbcopy) | grep errors

Это отправит весь вывод cat file.txt кому: pbcopy, и Вы только получите результат grep на Вашей консоли.

Можно вставить несколько процессов tee часть:

cat file.txt | tee >(pbcopy) >(do_stuff) >(do_more_stuff) | grep errors
249
27.01.2020, 19:27
  • 1
    Не беспокойство с pbcopy, но стоящий упоминания в целом: безотносительно выводов замены процесса также замечен следующим сегментом канала, после исходного входа; например: seq 3 | tee >(cat -n) | cat -e (cat -n нумерует входные строки, cat -e новые строки меток с $; Вы будете видеть это cat -e применяется и к исходному входу (сначала) и (затем) к выводу от cat -n). Вывод от нескольких замен процесса прибудет в недетерминированный порядок. –  mklement0 09.12.2014, 06:47
  • 2
    >( только работы в bash. Если Вы пробуете то использование, например, sh ничего не выйдет. Важно сделать это уведомление. –  AAlvz 16.12.2014, 18:31
  • 3
    @AAlvz: Положительная сторона: замена процесса не является функцией POSIX; dash, которые действуют как sh на Ubuntu, не поддерживает его, и даже сам Bash деактивирует функцию при вызове как sh или когда set -o posix в действительности. Однако это не просто Bash, который поддерживает замены процесса: ksh и zsh поддерживайте их также (не уверенный в других). –  mklement0 15.04.2015, 05:59
  • 4
    @mklement0, который, кажется, не верен. На zsh (Ubuntu 14.04) Ваша печать строки: 1 1 2 2 3 3$ 1$ 2$ 3, Который печален, потому что я действительно хотел, чтобы функциональность была, поскольку Вы говорите это. –  Aktau 21.10.2016, 12:56
  • 5
    @Aktau: Действительно, моя демонстрационная команда только работают, как описано в bash и ksh - zsh по-видимому, не отправляет вывод с выходных замен процесса на конвейер (возможно, это предпочтительно, потому что он не загрязняет то, что отправляется в следующий конвейерный сегмент - хотя он все еще печатает). Во всех упомянутых оболочках, однако, это обычно - не хорошая идея иметь единственный конвейер, в котором регулярный вывод stdout и вывод от замен процесса смешаны - выходное упорядочивание не будет предсказуемо способом, который может только нечасто появляться или с наборами данных крупносерийного производства. –  mklement0 22.10.2016, 08:39

Просто игра с заменой процесса.

mycommand_exec |tee >(grep ook > ook.txt) >(grep eek > eek.txt)

grep два двоичных файла, которые имеют тот же вывод от mycommand_exec как их процесс определенный вход.

17
27.01.2020, 19:27

Получите команду STDOUT к переменной и повторному использованию это так много раз, как Вам нравится:

commandoutput="$(command-to-run)"
echo "$commandoutput" | grep -i errors
echo "$commandoutput" | pbcopy

Если необходимо получить STDERR также, затем используйте 2>&1 в конце команды, как так:

commandoutput="$(command-to-run 2>&1)"
5
27.01.2020, 19:27
  • 1
    Где переменные хранятся? Если Вы имели дело с большим файлом или чем-то вроде того вида, не был бы этот пожиратель ресурсов большая память? Переменные ограничены в размере? –  cwd 07.01.2012, 06:09
  • 2
    , что, если $commandoutput огромен?, лучше для использования каналов и замены процесса. –  Nikhil Mulley 07.01.2012, 15:00
  • 3
    Очевидно, это решение возможно только, когда Вы будете знать, что размер вывода легко уместится в памяти, и Вы соглашаетесь с буферизацией всего вывода прежде, чем выполнить следующие команды на нем. Каналы решают эти две проблемы путем разрешения данных произвольной длины и потоковой передачи их в режиме реального времени к получателю, поскольку они сгенерированы. –  trr 23.06.2013, 15:15
  • 4
    Это - хорошее решение, если у Вас есть маленький вывод, и Вы знаете, что вывод будет текстом и не двоичный. (переменные оболочки часто не являются двоичным сейфом) –  Rucent88 20.07.2014, 07:48
  • 5
    я не могу заставить это работать с двоичными данными. Я думаю, что это - что-то с эхом, пытающимся интерпретировать пустые байты или некоторые другие несимвольные данные. –  Rolf 21.05.2017, 19:13

Можно указать несколько имен файлов к tee, и кроме того стандартный вывод может быть передан по каналу в одну команду. Для диспетчеризации вывода нескольким командам необходимо создать несколько каналов и указать каждого из них как один вывод tee. Существует несколько способов сделать это.

Замена процесса

Если Ваша оболочка является ksh93, ударом или zsh, можно использовать замену процесса. Это - способ передать канал команде, которая ожидает имя файла. Оболочка создает канал и передает имя файла как /dev/fd/3 к команде. Число является дескриптором файла, с которым подключен канал. Некоторые варианты Unix не поддерживают /dev/fd; на них именованный канал используется вместо этого (см. ниже).

tee >(command1) >(command2) | command3

Дескрипторы файлов

В любой оболочке POSIX можно использовать несколько дескрипторов файлов явно. Это требует варианта Unix, который поддерживает /dev/fd, начиная со всех кроме одного из выводов tee должен быть указан по имени.

{ { { tee /dev/fd/3 /dev/fd/4 | command1 >&9;
    } 3>&1 | command2 >&9;
  } 4>&1 | command3 >&9;
} 9>&1

Именованные каналы

Самый основной и портативный метод должен использовать именованные каналы. Оборотная сторона - то, что необходимо найти перезаписываемый каталог, создать каналы и вымыться впоследствии.

tmp_dir=$(mktemp -d)
mkfifo "$tmp_dir/f1" "$tmp_dir/f2"
command1 <"$tmp_dir/f1" & pid1=$!
command2 <"$tmp_dir/f2" & pid2=$!
tee "$tmp_dir/f1" "$tmp_dir/f2" | command3
rm -rf "$tmp_dir"
wait $pid1 $pid2
128
27.01.2020, 19:27
  • 1
    Большое спасибо за обеспечение двух альтернативных версий для тех, кто не хочет полагаться на удар или определенный ksh. –  trr 02.07.2013, 07:08
  • 2
    tee "$tmp_dir/f1" "$tmp_dir/f2" | command3 должен, конечно, быть command3 | tee "$tmp_dir/f1" "$tmp_dir/f2", поскольку Вы хотите stdout command3 переданный по каналу к tee, нет? Я протестировал Вашу версию под dash и tee блоки, неограниченно долго ожидая входа, но переключая порядок привели к ожидаемому результату. –  Adrian Günter 10.04.2018, 22:21
  • 3
    @AdrianGünter все три примера считывают данные со стандартного входа и отправляют его в каждый из command, command2 и command3. –  Gilles 'SO- stop being evil' 11.04.2018, 01:04
  • 4
    @Gilles я вижу, я неправильно истолковал намерение и попытался использовать отрывок неправильно. Спасибо за разъяснение! –  Adrian Günter 11.04.2018, 02:18

Если Вы используете zsh, то Вы можете воспользоваться возможностями функции MULTIOS, т.е. полностью избавиться от команды tee:

uname >file1 >file2

просто запишет выход uname в два разных файла: file1 и file2, что эквивалентно имени uname | tee file1 >file2

Аналогично перенаправление стандартных входов

wc -l <file1 <file2

эквивалентно cat file1 file2 | wc -l (пожалуйста, обратите внимание, что это не то же самое, что wc -l file1 file2, затем подсчитывает количество строк в каждом файле в отдельности).

Конечно, вы также можете использовать MULTIOS для перенаправления вывода не в файлы, а в другие процессы, используя подстановку процессов, например:

echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')
17
27.01.2020, 19:27

Это может быть полезно: http://www.spinellis.gr/sw/dgsh/ (оболочка ориентированного графа) Похоже на замену bash, поддерживающую более простой синтаксис для " команды multipipe.

1
27.01.2020, 19:27

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

Например, это может сделать следующий сценарий:

#!/bin/sh

temp=$( mktemp )
cat /dev/stdin > "$temp"

for arg
do
    eval "$arg" < "$temp"
done
rm "$temp"

Тестовый запуск в Ubuntu 16.04 с / bin / sh в качестве тире оболочки:

$ cat /etc/passwd | ./multiple_pipes.sh  'wc -l'  'grep "root"'                                                          
48
root:x:0:0:root:/root:/bin/bash
6
27.01.2020, 19:27

Вот быстрое -и -грязное частичное решение, совместимое с любой оболочкой, включая busybox.

Более узкая проблема, которую он решает, состоит в том, что :вывод полного stdoutна одну консоль и фильтрация на другой, без временных файлов или именованных каналов.

  • Начать другой сеанс на том же хосте. Чтобы узнать его имя TTY, введите tty. Предположим /dev/pty/2.
  • В первом сеансе запуститеthe_program | tee /dev/pty/2 | grep ImportantLog:

Вы получаете один полный журнал и один отфильтрованный.

1
27.01.2020, 19:27

Существует такжеpeeиз пакета moreutils . Он предназначен для этого:

pee 'command1' 'command2' 'cat -'
5
27.01.2020, 19:27

Другой вариант:

$ cat file.txt | tee >(head -1 1>&2) | grep foo

Работает путем перенаправления аргумента файла teeв подстановку процесса bash , где этот процесс — head, который печатает только одну строку (заголовка )и перенаправляет свой собственный вывод вstderr(чтобы было видно ).

1
23.09.2020, 13:16

Теги

Похожие вопросы