Делайте хвост -F до совпадения с рисунком
Я хочу сделать хвост -F для файла до совпадения с шаблоном. Я нашел способ с помощью awk, но ИМХО моя команда не совсем чистая. Проблема в том, что мне нужно сделать это только в одной строке из-за некоторых ограничений.
tail -n +0 -F /tmp/foo | \
awk -W interactive '{if ($1 == "EOF") exit; print} END {system("echo EOF >> /tmp/foo")}'
Хвост будет блокироваться, пока EOF не появится в файле. Это работает довольно хорошо. Блок END является обязательным, потому что "выход" awk не выходит сразу. Это заставляет awk вычислить блок END перед выходом. Блок END зависает при вызове чтения (из-за tail), поэтому последнее, что мне нужно сделать, - это написать еще одну строку в файле, чтобы принудительно завершить выполнение tail.
Кто-нибудь знает лучший способ сделать это?
10 ответов
Попробуй это:
sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
Вся командная строка завершится, как только строка "EOF" будет видна в /tmp/foo.
Есть один побочный эффект: хвостовой процесс будет запущен (в фоновом режиме), пока что-нибудь не будет записано в /tmp/foo.
Используйте опцию tail --pid, и tail остановится, когда оболочка умрет. Не нужно добавлять лишние к хвостовому файлу.
sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
У меня нет результатов с решением:
sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
Существует некоторая проблема, связанная с буфером, потому что, если к файлу не добавлено больше строк, sed не будет читать входные данные. Итак, проведя немного больше исследований, я придумал это:
sed '/EOF/q' <(tail -n 0 -f /tmp/foo)
Сценарий находится в https://gist.github.com/2377029
Это то, в чем Tcl довольно хорош. Если следующим является "tail_until.tcl",
#!/usr/bin/env tclsh
proc main {filename pattern} {
set pipe [open "| tail -n +0 -F $filename"]
set pid [pid $pipe]
fileevent $pipe readable [list handler $pipe $pattern]
vwait ::until_found
catch {exec kill $pid}
}
proc handler {pipe pattern} {
if {[gets $pipe line] == -1} {
if {[eof $pipe]} {
set ::until_found 1
}
} else {
puts $line
if {[string first $pattern $line] != -1} {
set ::until_found 1
}
}
}
main {*}$argv
Тогда вы бы сделали tail_until.tcl /tmp/foo EOF
Это работает для вас?
tail -n +0 -F /tmp/foo | sed '/EOF/q'
Я предполагаю, что "EOF" - это шаблон, который вы ищете. sed
команда завершается, когда находит его, а это означает, что tail
должен выйти в следующий раз, когда он пишет.
Я полагаю, что есть шанс, что tail
будет зависать, если шаблон будет найден в конце файла, ожидая, когда в файле появится больше выходных данных, которые никогда не появятся. Если это действительно проблема, вы, вероятно, могли бы организовать ее уничтожение - конвейер в целом завершится, когда sed
завершается (если вы не используете забавную оболочку, которая решает, что это не правильное поведение).
Злоба о Баш
Как боялись, bash
(по крайней мере, на MacOS X, но, вероятно, везде) - это оболочка, которая думает, что ей нужно зависать в ожидании tail
закончить, хотя sed
уволиться. Иногда - чаще, чем мне нравится - я предпочитаю поведение старой доброй оболочки Bourne, которая была не такой умной и потому реже угадывалась, чем Bash. dribbler
это программа, которая выводит сообщения по одному в секунду (в данном примере '1: Hello' и т. д.), и вывод выводится на стандартный вывод. В Bash эта последовательность команд висит, пока я не сделал 'echo pqr >>/tmp/foo
'в отдельном окне.
date
{ timeout -t 2m dribbler -t -m Hello; echo EOF; } >/tmp/foo &
echo Hi
sleep 1 # Ensure /tmp/foo is created
tail -n +0 -F /tmp/foo | sed '/EOF/q'
date
К сожалению, я не сразу вижу возможность контролировать это поведение. Я нашел shopt lithist
, но это не связано с этой проблемой.
Ура для Korn Shell
Я отмечаю, что когда я запускаю этот скрипт с использованием оболочки Korn, он работает так, как я ожидал - оставляя tail
таится вокруг, чтобы быть убитым как-то. То, что работает, есть 'echo pqr >> /tmp/foo
после второй даты команда завершена.
Вот расширенная версия решения Джона, которая использует sed вместо grep, так что вывод tail переходит к stdout:
sed -r '/EOF/q' <( exec tail -n +0 -f /tmp/foo ); kill $! 2> /dev/null
Это работает, потому что sed создается перед tail, поэтому $! держит PID хвоста
Основное преимущество этого решения по сравнению с решениями sh -c заключается в том, что уничтожение sh, по-видимому, приводит к выводу чего-либо на вывод, например, "прекращено", что нежелательно.
sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
Здесь главная проблема с $$
, Если вы запустите команду как есть, $$
устанавливается не в sh, а в PID текущей оболочки, в которой выполняется команда.
Чтобы убить работу, вам нужно изменить kill $$
в kill \$$
После этого вы можете безопасно избавиться от --pid=$$
перешел к хвостовой команде.
Подводя итог, следующее будет работать просто отлично:
/bin/sh -c 'tail -n 0 -f /tmp/foo | { sed "/EOF/ q" && kill \$$ ;}
При желании вы можете пройти -n
в sed
молчать:)
Убить висячего tail
процесс, а также вы можете выполнить tail
команда в контексте замены процесса (Bash), которая впоследствии может быть уничтожена, как если бы это был фоновый процесс. (Код взят из Как прочитать одну строку из tail -f через конвейер, а затем завершить?).
: > /tmp/foo
grep -m 1 EOF <( exec tail -f /tmp/foo ); kill $! 2> /dev/null
echo EOF > /tmp/foo # terminal window 2
В качестве альтернативы вы можете использовать именованный канал.
(
: > /tmp/foo
rm -f pidfifo
mkfifo pidfifo
sh -c '(tail -n +0 -f /tmp/foo & echo $! > pidfifo) |
{ sed "/EOF/ q" && kill $(cat pidfifo) && kill $$ ;}'
)
echo EOF > /tmp/foo # terminal window 2
Готов к использованию для tomcat =
sh -c 'tail -f --pid = $$ catalina.out | {grep -i -m 1 "Запуск сервера в" && kill $$;}'
для вышеуказанного сценария:
sh -c 'tail -f --pid=$$ /tmp/foo | { grep -i -m 1 EOF && kill $$ ;}'