Завершение хвоста -f началось в сценарии оболочки - PullRequest
25 голосов
/ 11 января 2010

У меня есть следующее.

  1. Процесс Java записывает логи в стандартный вывод
  2. Сценарий оболочки, запускающий процесс Java
  3. Еще один сценарий оболочки, который выполняет предыдущий и перенаправляет журнал
  4. Я проверяю файл журнала с помощью команды tail -f для сообщения об успехе.

Даже если у меня есть выход 0 в коде, я не могу завершить процесс tail -f.

Что не позволяет моему сценарию завершиться. Есть ли другой способ сделать это в Bash?

Код выглядит следующим образом.

function startServer() {
  touch logfile
  startJavaprocess > logfile &

  tail -f logfile | while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      exit 0
    fi
  done
}

Ответы [ 18 ]

25 голосов
/ 11 января 2010

Лучший ответ, который я могу придумать, это

  1. Установите время ожидания для чтения, tail -f logfile | read -t 30 line
  2. Начните хвост с --pid=$$, таким образом, он завершится, когда завершится процесс bash.

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

Не забудьте запустить свой хвост перед сервером.

tail -n0 -F logfile 2>/dev/null | while read -t 30 line

-F будет «читать» файл, даже если он не существует (начните читать его, когда он появится). -n0 не будет читать ничего, уже находящегося в файле, поэтому вы можете продолжать добавлять файл журнала вместо того, чтобы каждый раз перезаписывать его, и к стандартному ротационному журналу на нем.

EDIT:
Итак, довольно грубое «решение», если вы используете хвост. Возможно, есть лучшие решения, использующие что-то еще, кроме хвоста, но я должен вам это дать, хвост довольно хорошо вытащил вас из сломанной трубы. «Тройник», который способен обрабатывать SIGPIPE, вероятно, будет работать лучше. Процесс java, активно выполняющий удаление файловой системы с каким-либо сообщением «im alive», вероятно, еще проще подождать.

function startServer() {
  touch logfile

  # 30 second timeout.
  sleep 30 &
  timerPid=$!

  tail -n0 -F --pid=$timerPid logfile | while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      # stop the timer..
      kill $timerPid
    fi
  done &

  startJavaprocess > logfile &

  # wait for the timer to expire (or be killed)
  wait %sleep
}
7 голосов
/ 30 января 2011

Исходя из ответов, которые я нашел здесь, это то, что я придумал.

Он напрямую работает с tail и убивает его, как только мы увидим необходимый вывод журнала. Использование 'pkill -P $$ tail' должно гарантировать, что правильный процесс убит.

wait_until_started() {
    echo Waiting until server is started
    regex='Started'
    tail logfile -n0 -F | while read line; do
            if [[ $line =~ $regex ]]; then
                    pkill -9 -P $$ tail
            fi
    done
    echo Server is started
}
6 голосов
/ 11 января 2010

Согласно справочной странице tail , вы можете завершить хвост после того, как процесс умирает

В BASH вы можете получить PID последнего запущенного фонового процесса, используя $! ТАК если вы используете bash:

tail -f --pid=$! logfile
2 голосов
/ 20 августа 2012

У меня была похожая ситуация, когда мне нужно было привязать журнал для "запущенного" сообщения в течение разумного времени, и если оно не было найдено в течение этого времени, мне нужно выйти. Вот что я в итоге сделал.

wait_tomcat_start(){
WAIT=60
echo "Waiting for Tomcat to initialize for $WAIT seconds"

# Tail log file, do a while read loop with a timeout that checks for desired log status,
# if found kill the find and break the loop. If not found within timeout: the read -t will
# kill the while read loop and bounce to the OR statement that will in turn kill the tail 
# and echo some message to the console.
tail -n0 -f $SERVERLOG | while read -t $WAIT LINE || (pkill -f "tail -n0 -f" && echo "Tomcat did not start in a timely fashion! Please check status of tomcat!!!")
do
        echo "$LINE"
        [[ "${LINE}" == *"Server startup in"* ]] && pkill -f "tail -n0 -f" && break
done
}

Я не уверен, что это очень элегантный или даже лучший способ сделать это, но он работает достаточно хорошо для меня. Буду рад любым мнениям:)

2 голосов
/ 11 января 2010

Захват pid фонового процесса

pid=$!

Используйте параметр tail - pid = PID , чтобы он завершался после завершения процесса, имеющего pid $ PID.

1 голос
/ 11 января 2010

Как насчет использования бесконечного цикла вместо опции командной строки -f для tail?

function startServer() {
  startJavaprocess > logfile &

  while [ 1 ]
  do
   if tail logfile | grep -q 'Started'; then
    echo 'Server started'
    exit 0
   fi
  done
}
1 голос
/ 11 января 2010

Вместо того, чтобы выйти из процесса, вы можете вместо этого найти идентификатор процесса tail -f и уничтожить его (kill -9 будет даже безопасным, если вы уверены, что файл журнала завершен).

Таким образом, while read line завершится естественным образом, и вам не нужно будет выходить.

Или, поскольку вы на самом деле не используете tail для вывода на экран, вы также можете попробовать более старую школу:

grep -q 'Started' logfile
while [[ $? -ne 0 ]] ; do
    sleep 1
    grep -q 'Started' logfile
done
1 голос
/ 22 октября 2011

У меня была такая же проблема, я не мог найти простое и хорошее решение. Я не очень хорош в Python, но мне удалось как-то решить эту проблему:

wait_log.py

#!/usr/bin/env python

from optparse import OptionParser
import os
import subprocess
import time

def follow(file):
    def file_size(file):
        return os.fstat(file.fileno())[6]
    def is_newLine(line):
        return line != None and line.find("\n") != -1;

    file.seek(0, os.SEEK_END)

    while True:
        if file.tell() > file_size(file):
            file.seek(0, os.SEEK_END)

        line_start = file.tell()
        line = file.readline()

        if is_newLine(line):
            yield line
        else:
            time.sleep(0.5)
            file.seek(line_start)

def wait(file_path, message):
    with open(file_path) as file:
        for line in follow(file):
            if line.find(message) != -1:
                break

def main():
    parser = OptionParser(description="Wait for a specific message in log file.", usage="%prog [options] message")
    parser.add_option("-f", "--file", help="log file")

    (options, args) = parser.parse_args()

    if len(args) != 1:
        parser.error("message not provided")

    if options.file == None:
        parser.error("file not provided")

    wait(options.file, args[0])

if __name__ == "__main__":
    main()
1 голос
/ 31 июля 2012

Была похожая проблема, когда хвостовой процесс не был убит, когда

  1. Пробежать через jsch
  2. tail не производил никакого вывода в jsch и, следовательно, в свой выходной поток.

Использовал --pid=$!, чтобы убить его, и запустил бесконечный цикл while, чтобы отразить что-то на заднем плане перед хвостом, который убит, когда основной процесс убит, и таким образом убьет хвост.

( while true; do echo 'running';  sleep 5; done ) & ( tail -f --pid=$! log-file )
1 голос
/ 21 марта 2016

Можно выполнить фон tail -f logfile, отправить tailpid в подоболочку цикла while read и реализовать trap на EXIT для уничтожения команды tail.

( (sleep 1; exec tail -f logfile) & echo $! ; wait) | (
  trap 'trap - EXIT; kill "$tailpid"; exit' EXIT
  tailpid="$(head -1)"
  while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      exit 0
    fi
  done
)
...