Подтвердить что ты не робот

Сделайте хвост -F до соответствия рисунку

Я хочу сделать хвост -F в файле до соответствия шаблону. Я нашел способ использовать awk, но IMHO моя команда не очень чистая. Проблема в том, что мне нужно делать это только в одной строке из-за некоторых ограничений.

tail -n +0 -F /tmp/foo | \
awk -W interactive '{if ($1 == "EOF") exit; print} END {system("echo EOF >> /tmp/foo")}'

Хвост блокируется до тех пор, пока в файле не появится EOF. Это работает очень хорошо. Блок END является обязательным, так как awk "exit" не выходит сразу. Это делает awk для проверки блока END перед выходом из игры. Блок END зависает при вызове чтения (из-за хвоста), поэтому последнее, что мне нужно сделать, это написать еще одну строку в файле, чтобы заставить хвост выйти.

Знает ли кто-нибудь лучший способ сделать это?

4b9b3361

Ответ 1

Попробуйте следующее:

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

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

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

Ответ 2

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

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

Ответ 3

Я не получаю решения:

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

Существует некоторая проблема, связанная с буфером, потому что если в файл добавлено больше строк, то sed не будет считывать ввод. Итак, с небольшим количеством исследований я придумал следующее:

sed '/EOF/q' <(tail -n 0 -f /tmp/foo)

script находится в https://gist.github.com/2377029

Ответ 4

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

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

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

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


Grump около Bash

Как опасался, bash (на MacOS X, по крайней мере, но, вероятно, везде) - это оболочка, которая думает, что она должна стоять в ожидании завершения tail, даже если sed quit. Иногда - чаще, чем мне нравится - я предпочитаю поведение старой доброй раковины Борна, которая была не такой умной и поэтому догадывалась неправильно менее часто, чем 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, но не связанный с этой проблемой.

Ура для оболочки Корна

Я отмечаю, что когда я запускаю этот script с помощью оболочки Korn, он работает так, как я ожидал, оставляя tail скрываясь, чтобы его каким-то образом убили. Что работает там 'echo pqr >> /tmp/foo' после завершения второй команды даты.

Ответ 5

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

Ответ 6

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

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

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

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

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

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

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

Ответ 7

Здесь расширенная версия решения Jon, которая использует sed вместо grep, так что выход хвоста идет в stdout:

sed -r '/EOF/q' <( exec tail -n +0 -f /tmp/foo ); kill $! 2> /dev/null

Это работает, потому что sed создается до хвоста так $! содержит PID хвоста

Основным преимуществом этого решения sh-c является то, что убийство sh, похоже, что-то печатает на выходе, например "Terminated", которое нежелательно

Ответ 8

Чтобы убить оборванный процесс tail, вы можете выполнить команду tail в контексте подстановки процесса (Bash), который впоследствии может быть убит, как если бы это был фоновый процесс. (Код, взятый из Как читать одну строку из "хвоста -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

Ответ 9

tail -f <filename> | grep -q "<pattern>"