Это то же самое, что и в:
В:
cmd1 | cmd2
Ваша оболочка ожидает завершения cmd1
даже после завершения cmd2
. Это касается не всех оболочек. Например, некоторые, такие как оболочка Bourne или Korn, этого не делают.
Когда cmd2
умирает, конвейер на cmd1
stdout становится сломанным , но это не прекращается cmd1
мгновенно.
cmd1
завершится при следующей попытке записи в этот канал. Затем он получит SIGPIPE, действие которого по умолчанию — завершить процесс.
С помощью cmd1
== tail -f file
и cmd2
== sed /w/q
, tail -f
будет считывать последние 10 строк файла и записывать их на стандартный вывод (канала ), обычно в один фрагмент, если только строки не очень большие, и ждать, пока к file
будет добавлено больше текста.
sed
, который выполняется параллельно, будет ожидать ввода на своем стандартном вводе, читать его, обрабатывать строку за строкой и завершать работу, если есть строка, содержащая w
.
Как только (или, возможно, с задержкой в одну -строку с некоторой sed
реализацией ), когда она находит эту строку, она завершается, но в это время tail
уже записала в канал все, что он должен был ЗАПИСАТЬ, поэтому он не получит SIGPIPE, если какой-либо дополнительный текст не будет добавлен позже в файл (, после чего он сделает фатальныйwrite()
).
Если вы хотите, чтобы cmd1
завершилось, как только cmd2
завершится,вам нужно что-то, чтобы убить его, как только cmd2
завершится. Как с:
sh -c 'echo "$$"; exec tail -f test.txt' | {
IFS= read pid
sed /w/q
kill -s PIPE "$pid"
}
Или сbash
:
{ sed /w/q; kill -s PIPE "$!"; } < <(exec tail -f text.txt)
Как заметил @user414777, начиная с версии 8.28, реализация GNU tail
в режимах отслеживания теперь не только опрашивает новые данные в отслеживаемом файле (s ), но также проверяет, соответствует ли его стандартный вывод становится сломанной трубой и выходит сразу после этого (или в течение одной секунды, если inotify не используется ), что делает обходные пути, описанные выше, ненужными.
Однако обратите внимание, что только GNU tail
выполняет такую обработку, а не какая-либо другая реализацияtail
(AFAIK )и никакая другая утилита (, даже реализации GNU ).
Итак, пока:
tail -f file | head -n1
Выйдет через одну строку,
tail -f file | tr '[:lower:]' '[:upper:]' | head -n1
, например, не обязательно, поскольку tr
не будет отслеживать, станет ли его стандартный вывод сломанным каналом, и поэтому умрет, только если он напишет что-то там после того, как канал, как обычно, сломался (и только в этот момент GNU tail -f
завершит работу ).