Делать хвост -F до совпадения с рисунком - PullRequest
22 голосов
/ 17 февраля 2011

Я хочу сделать хвост -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 ]

34 голосов
/ 25 февраля 2011

Используйте параметр tail --pid, и tail остановится, когда оболочка умрет.Нет необходимости добавлять дополнительные к файлу с хвостами.

sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
29 голосов
/ 17 февраля 2011

Попробуйте это:

sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

Вся командная строка завершится, как только строка "EOF" будет обнаружена в /tmp/foo.

Есть один побочный эффект: хвостовой процесс будет запущен (в фоновом режиме), пока что-нибудь не будет записано в /tmp/foo.

.
8 голосов
/ 13 апреля 2012

У меня нет результатов с решением:

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

4 голосов
/ 17 февраля 2011

Это то, в чем 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

4 голосов
/ 17 февраля 2011

Это работает для вас?

tail -n +0 -F /tmp/foo | sed '/EOF/q'

Я предполагаю, что EOF - это шаблон, который вы ищете.Команда sed завершает работу при обнаружении, что означает, что tail должен завершиться при следующей записи.

Я полагаю, что есть внешний шанс, что tail будет зависать, если шаблоннаходится примерно в конце файла, ожидая, пока в файле появятся дополнительные выходные данные, которые никогда не появятся.Если это действительно проблема, вы, возможно, могли бы организовать ее уничтожение - конвейер в целом завершится, когда sed завершится (если вы не используете забавную оболочку, которая решает, что это не правильное поведение).


ворчание по поводу Bash

Как и опасалось, 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' после завершения второй команды date.

2 голосов
/ 28 октября 2011

Вот расширенная версия решения Джона, которая использует 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, по-видимому, приводит к выводу чего-либо на вывод, например, «прекращено», что нежелательно

2 голосов
/ 06 мая 2011
sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

Здесь главная проблема с $$. Если вы запускаете команду как есть, $$ устанавливается не на sh, а на PID текущей оболочки, в которой выполняется команда.

Чтобы убить сработало, вам нужно изменить kill $$ на kill \$$

После этого вы можете безопасно избавиться от --pid=$$, переданного команде tail.

Подводя итог, следующее будет работать нормально:

/bin/sh -c 'tail -n 0 -f /tmp/foo | { sed "/EOF/ q" && kill \$$ ;}

При желании вы можете передать -n на sed, чтобы сохранить молчание :)

1 голос
/ 14 февраля 2018

готов к использованию для 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 $$ ;}'
1 голос
/ 18 сентября 2011

Чтобы также убить висячий процесс 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
0 голосов
/ 16 октября 2015
tail -f <filename> | grep -q "<pattern>"
...