
Я попал в интересное состояние на Ubuntu. Нижеприведенные шаги описывают это лучше всего.
С одной трубой я вижу то, что и ожидал
# In shell A
tail -f foo.log | grep aaa
# In shell B
echo aaa >> foo.log
# Shell A prints out `aaa`
Но с несколькими трубами я вообще ничего не вижу.
# In shell A
tail -f foo.log | grep aaa | grep bbb
# In shell B
echo aaa bbb >> foo.log
# Nothing ever prints in shell A
Но если я просто повторяю, то всё работает нормально.
echo 'aaa bbb' | grep aaa | grep bbb
Это моя попытка создать минимальное воспроизведение -- изначально я столкнулся с проблемой, пытаясь получить логи из adb logcat (Android dev tools). Я также пробовал в zsh, bash и fish.
Я предполагал, что это как-то связано с моим лимитом наблюдателей inotify, но его увеличение ничего не изменило.
решение1
Это происходит из-за буферизации в канале, который, как правило, не заботится об очередях и может накапливать данные.
Я думаю, tail -f
что использует буферизацию строк сама по себе; и последний grep
пишет в tty, так что он также использует буферизацию строк. Поэтому ваш первый пример работает.
Но grep
в середине отличается, и вам нужно настроить его поведение, принудительно включив буферизацию строк или отключив буферизацию. Следующие команды будут работать так, как вы ожидали.
Если у вас
grep
поддерживается--line-buffered
(в Ubuntu так и есть):tail -f foo.log | grep --line-buffered aaa | grep bbb
Более общие решения (они будут работать со многими фильтрами, кроме
grep
):tail -f foo.log | unbuffer -p grep aaa | grep bbb tail -f foo.log | stdbuf -oL grep aaa | grep bbb tail -f foo.log | stdbuf -o0 grep aaa | grep bbb
Подробности и особенности см . здесь man 1 grep
.man 1 unbuffer
man 1 stdbuf
Примечания:
- Ни одно из решений не является переносимым (
grep --line-buffered
и не специфицировано в POSIX).unbuffer
stdbuf
- Если вы можете сделать это с,
grep --line-buffered
то это должен быть ваш выбор. Нет смысла использовать дополнительные инструменты. - Сопутствующий вопрос по Unix и Linux SE:Отключить буферизацию в канале.
unbuffer
иstdbuf
работают совершенно по-разному.- В данном случае
stdbuf
буферизация строк (-oL
) должна быть предпочтительнее отсутствия буферизации (-o0
), поскольку- он, скорее всего, работает лучше,
- и другие части вашего канала в любом случае используют буферизацию линий.
- Если ваш последний
grep
записал в еще один файл, он будет вести себя как другойgrep
. В таком случае, если вы хотите, чтобы строки немедленно появлялись в конечном файле, то вам следует также изменить поведение последнегоgrep
. - В
fish
, еслиgrep
это функция-обертка, вы можете не получить желаемого поведения с--line-buffered
. Используйтеcommand grep --line-buffered
. См. этот вопрос:Выходной канал ожидает EOF вfish
.
Примечание: tail foo.log | grep aaa | grep bbb
(т.е. tail
без -f
) не вызывает проблему, потому что tail
выходит. При tail
выходе первый grep
обнаруживает EOF, очищает свой буфер и выходит, затем второй grep
делает то же самое.