Объедините вывод двух параллельных программ с bash
Я хочу захватить вывод двух параллельных программ (хвосты на лог файлах) в один выходной поток в bash.
Я использовал эту примерную программу для тестирования:
function foo { for i in $(seq 1 10); do echo "program $*"; sleep 1; done }
Теперь работает отлично
(foo bar & foo baz &) | tee /tmp/output
но как только я добавлю дополнительную трубку в микс, она больше не работает:
(foo bar | grep bar & foo baz &) | tee /tmp/output # does't work
Выход становится последовательным. Я мог бы сделать отдельную программу, которая включает grep, но я хотел бы знать, есть ли способ обойти это.
Если кто-то может объяснить, почему это не работает, я был бы очень счастлив.
Ответы
Ответ 1
Отличный вопрос! Это меня немного заглохло, но я думаю, что знаю, что происходит. Случается, что grep
выполняет буферизацию вывода. Итак, если вы позволите этому запустить, вы увидите, что все это наводнение в конце. Если вы используете GNU grep
, попробуйте передать параметр -line-buffered:
(foo bar | grep --line-buffered bar & foo baz &) | tee /tmp/output
Чтобы угадать догадки и подумать, что, по сути, это то, что я сказал, что grep
выполняет буферизацию большего объема вывода, потому что isatty(1)
указывает, что он не пишет TTY (даже если вы смотрите вывод на TTY через tee
). Буферизируя больше вывода, он делает меньше вызовов write()
и более эффективен. Знакомое поведение запуска grep
и просмотр вывода в терминале - это строка буферизации - строки появляются по мере их нахождения. Этот параметр заставляет grep
работать в этом режиме.
Имейте в виду, что, как предупреждает справочная страница, это может повлиять на производительность на grep
.
Ответ 2
Из-за использования канала между foo bar
и grep
вы вызываете grep для ожидания вывода команды foo bar
, и поэтому все строки с bar
поступают сразу после строк baz. Если вы действительно хотите grep что-то из команды, тогда определите другую функцию, подобную этой:
function foo1 { for i in {1..3}; do echo "program $*" | grep "$*"; sleep 1; done }
И затем выполните:
(foo1 bar & foo baz &) | tee /tmp/output
Теперь вы заметите, что вывод выглядит следующим образом:
program baz
program bar
program baz
program bar
program baz
program bar
...
...