Linux: блокировка до совпадения строки в файле («tail + grep with blocking») - PullRequest
11 голосов
/ 23 июня 2011

Есть ли какой-либо однострочный способ в инструментах bash / GNU блокировать, пока в файле не найдется соответствующая строка? В идеале с таймаутом. Я хочу избежать многострочного цикла.

Обновление: Похоже, мне следовало бы подчеркнуть, что я хочу, чтобы процесс завершился при совпадении строки.

Ответы [ 7 ]

22 голосов
/ 23 июня 2011

Спасибо обоим за ответы, но важной частью было то, что процесс блокируется, пока не найден, а затем завершается. Я нашел это:

grep -q 'PATTERN' <(tail -f file.log)

-q не слишком переносимый, но я буду использовать только Red Hat Enterprise Linux, так что все в порядке. И с таймаутом:

timeout 180 grep -q 'PATTERN' <(tail -f file.log)
6 голосов
/ 13 апреля 2012

Я создаю вариант с помощью sed вместо grep, печатая все проанализированные строки.

sed '/PATTERN/q' <(tail -n 0 -f file.log)

Сценарий в https://gist.github.com/2377029

3 голосов
/ 05 августа 2011

Посмотрите на параметр --max-count:

tail -f file.log | grep -m 1 'PATTERN'

Он выйдет после первой строки, которая соответствует PATTERN.


РЕДАКТИРОВАТЬ : обратите внимание на комментарий @ Karoly ниже.Если скорость file.log низкая, возможно, процесс grep будет блокироваться до тех пор, пока в файл не будет добавлено дополнительное содержимое после соответствующей строки.

echo 'context PATTERN line' >> file.log  ## grep shows the match but doesn't exit

напечатаетсовпадающая строка, но она не завершится, пока в файл не будет добавлено дополнительное содержимое (даже если у него еще нет новой строки):

echo -n ' ' >> file.log  ## Now the grep process exits

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

Также обратите внимание, что такое поведение не происходит при чтении из консоликак в stdin, похоже, что разница в способе, который grep читает из канала:

$ grep -m1 'PATTERN' -      # manually type PATTERN and enter, exits immediately
$ cat | grep -m1 'PATTERN'  # manually type PATTERN and enter, and it hangs
2 голосов
/ 23 июня 2011
$ tail -f path | sed /pattern/q

или, если вы хотите подавить вывод несоответствующих строк:

$ tail -f path | sed -n '/pattern/{p; q;}'

Простой способ добавить время ожидания - сделать:

$ cmd& sleep 10; kill $! 2> /dev/null

(Подавить ошибки при уничтожении, чтобы, если процесс завершается до истечения времени, вы не получили предупреждение «Нет такого процесса»).Обратите внимание, что это совсем не надежно, так как возможно, что cmd будет прерван, и счетчик pid будет свернут, и некоторые другие команды получат этот pid к моменту истечения таймера.

2 голосов
/ 23 июня 2011
tail -f file | grep word | head -n1

Будет публиковать снип с асинхронным тайм-аутом

На данный момент: Как включить таймер в Bash Scripting?

Связанный ответ определяет 'run_or_timeout'функция, которая делает то, что вы ищете, очень хитрый способ

0 голосов
/ 11 июля 2016

У меня было похожее требование, и я получил следующее:

Одна строка, за которой вы следите, это строка, которая начинается с "timeout ....", а остальная часть кода - подготовкаработа, необходимая для предоставления однострочнику необходимой информации и последующей очистки.

##
## Start up the process whose log file we want to monitor for a specific pattern.
##
touch file_to_log_nohup_output.log
nohup "some_command" "some_args" >> file_to_log_nohup_output.log 2>&1 &
my_cmd_pid=$!


## Specify what our required timeout / pattern and log file to monitor is
my_timeout=10m
my_logfile="/path/to/some_command's/log/file.txt"
my_pattern="Started all modules."


## How does this work?
## - In a bash sub shell, started in the background, we sleep for a second and
##   then execute tail to monitor the application's log file.
## - Via the arguments passed to it, tail has been configured to exit if the
##   process whose log file it is monitoring dies.
## - The above sub shell, is executed within another bash sub shell in which
##   we identify the process id of the above sub shell and echo it to stdout.
## - Lastly, in that sub shell we wait for the sub shell with tail running in
##   it as a child process, to terminate and if it does terminate, we redirect
##   any output from its stderr stream to /dev/null.
## - The stdout output of the above sub shell is piped into another sub shell
##   in which we setup a trap to watch for an EXIT event, use head -1 to read
##   the process id of the tail sub shell and finally start a grep process
##   to grep the stdout for the requested pattern. Grep will quit on the first
##   match found. The EXIT trap will kill the process of the tail sub shell
##   if the sub shell running grep quits.
##
## All of this is needed to tidy up the monitoring child processes for
## tail'ing + grep'ing the application log file.
##
## Logic of implementing the above sourced from: http://superuser.com/a/1052328


timeout ${my_timeout} bash -c '((sleep 1; exec tail -q -n 0 --pid=$0 -F "$1" 2> /dev/null) & echo $! ; wait $! 2>/dev/null ) | (trap "kill \${my_tail_pid} 2>/dev/null" EXIT; my_tail_pid="`head -1`"; grep -q "$2")' "${my_cmd_pid}" "${my_logfile}" "${my_pattern}" 2>/dev/null &


##
## We trap SIGINT (i.e. when someone presses ctrl+c) to clean up child processes.
##
trap 'echo "Interrupt signal caught. Cleaning up child processes: [${my_timeout_pid} ${my_cmd_pid}]." >> "file_to_log_nohup_output.log"; kill ${my_timeout_pid} ${my_cmd_pid} 2> /dev/null' SIGINT
wait ${my_timeout_pid}
my_retval=$?
trap - SIGINT


## If the time out expires, then 'timeout' will exit with status 124 otherwise
## it exits with the status of the executed command (which is grep in this
## case).
if [ ${my_retval} -eq 124 ]; then
    echo "Waited for [${my_timeout}] and the [${my_pattern}] pattern was not encountered in application's log file."
    exit 1
else
    if [ ${my_retval} -ne 0 ]; then
        echo "An issue occurred whilst starting process. Check log files:"
        echo "  * nohup output log file: [file_to_log_nohup_output.log]"
        echo "  * application log file: [${my_logfile}]"
        echo "  * application's console log file (if applicable)"
        exit 1
    else
        info_msg "Success! Pattern was found."
        exit 0
    fi
fi

Я реализовал вышеизложенное в автономном скрипте, который можно использовать для запуска команды wait forего файл журнала должен иметь требуемый шаблон с таймаутом.

Доступно здесь: run_and_wait.sh

0 голосов
/ 04 августа 2014

ждать появления файла

while [ ! -f /path/to/the.file ] 
do sleep 2; done

дождаться появления строки в файле

while ! grep "the line you're searching for" /path/to/the.file  
do sleep 10; done

https://superuser.com/a/743693/129669

...