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

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

Я больше всего знаком с OS X, и поэтому есть два, которые сразу приходят в голову: pbcopy и pbpaste), которые являются средством доступа к системному буферу.

Во всяком случае, я знаю, что если я хочу взять stdout и выплюнуть вывод, чтобы перейти к stdout и файлу, то я могу использовать команду tee. И я немного знаю о xargs, но я не думаю, что это то, что я ищу.

Я хочу знать, как я могу разделить stdout, чтобы перейти между двумя (или более) командами. Например:

cat file.txt | stdout-split -c1 pbcopy -c2 grep -i errors

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

Кроме того - я не спрашиваю, как отправить это в файл, и stdout - это может быть «дублированный» вопрос (извините), но я сделал некоторые поиски и мог найти только похожие, которые спрашивали о том, как разделить между stdout и файлом - и ответы на эти вопросы казались tee, которые, как я думаю, не будут работать для меня.

Наконец, вы можете спросить: «Почему бы просто не сделать pbcopy последним в цепочке труб?» и мой ответ 1) что, если я хочу использовать его и все еще видеть вывод в консоли? 2) что, если я хочу использовать две команды, которые не выводят stdout после обработки ввода?

О, и еще одно - я понимаю, что могу использовать tee и именованный канал (mkfifo)), но я надеялся, что это можно сделать встроенным, сжато, без предварительной настройки:)

149 голосов | спросил cwd 7 Jam1000000amSat, 07 Jan 2012 07:32:11 +040012 2012, 07:32:11

8 ответов


197

Вы можете использовать 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
ответил Mat 7 Jpm1000000pmSat, 07 Jan 2012 13:29:16 +040012 2012, 13:29:16
103

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

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

Если ваша оболочка - ksh93, bash или 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
ответил Gilles 20 J000000Friday12 2012, 02:38:21
15

Если вы используете 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/')
ответил jimmij 22 MonEurope/Moscow2014-12-22T04:24:25+03:00Europe/Moscow12bEurope/MoscowMon, 22 Dec 2014 04:24:25 +0300 2014, 04:24:25
14

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

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

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

ответил Nikhil Mulley 7 Jpm1000000pmSat, 07 Jan 2012 16:59:58 +040012 2012, 16:59:58
6

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

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

#!/bin/sh

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

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

Тестирование на Ubuntu 16.04 с помощью /bin/sh в качестве оболочки dash:

$ cat /etc/passwd | ./multiple_pipes.sh  'wc -l'  'grep "root"'                                                          
48
root:x:0:0:root:/root:/bin/bash
ответил Sergiy Kolodyazhnyy 5 FebruaryEurope/MoscowbSun, 05 Feb 2017 22:38:11 +0300000000pmSun, 05 Feb 2017 22:38:11 +030017 2017, 22:38:11
5

Запишите команду STDOUT в переменную и повторно используйте ее столько раз, сколько хотите:

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

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

commandoutput="$(command-to-run 2>&1)"
ответил laebshade 7 Jam1000000amSat, 07 Jan 2012 07:43:37 +040012 2012, 07:43:37
0

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

ответил sivann 13 TueEurope/Moscow2016-12-13T21:14:16+03:00Europe/Moscow12bEurope/MoscowTue, 13 Dec 2016 21:14:16 +0300 2016, 21:14:16
0

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

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

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

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

ответил Victor Sergienko 15 MaramThu, 15 Mar 2018 02:53:35 +03002018-03-15T02:53:35+03:0002 2018, 02:53:35

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132