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

Окончание хвоста -f запускается в оболочке script

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

  • Запись Java-процессов, записывающая в stdout
  • Оболочка script, начинающая процесс Java
  • Еще одна оболочка script, которая выполняет предыдущую и перенаправляет журнал
  • Я проверяю файл журнала командой tail -f для сообщения об успешном завершении.

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

Это не позволяет завершить мой script. Есть ли другой способ сделать это в 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
}
4b9b3361

Ответ 1

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

  • Поставьте таймаут на чтение, tail -f logfile | read -t 30 line
  • Запустить хвост с --pid=$$, таким образом он выйдет, когда завершится bash -process.

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

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

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

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

EDIT:
Итак, довольно грубое "решение", если вы используете хвост. Есть, вероятно, лучшие решения, используя что-то еще, но хвост, но я должен отдать его вам, хвост достает вас из сломанной трубы довольно хорошо. "Tee", который способен обрабатывать 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
}

Ответ 2

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

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

tail -f --pid=$! logfile

Ответ 3

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

Он напрямую касается хвоста и убивает его, как только мы увидим необходимый выход журнала. Использование "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
}

Ответ 4

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

pid=$!

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

Ответ 5

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

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
}

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

Ответ 6

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

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

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

grep -q 'Started' logfile
while [[ $? -ne 0 ]] ; do
    sleep 1
    grep -q 'Started' logfile
done

Ответ 7

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

function startServer() {
  startJavaprocess > logfile &

  while [ 1 ]
  do
   if tail logfile | grep -q 'Started'; then
    echo 'Server started'
    exit 0
   fi
  done
}

Ответ 8

У меня была та же проблема, я не мог найти простое и хорошее решение. Я не очень хорош в 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()

Ответ 9

Имел подобную проблему, когда процесс хвоста не был убит, когда

  • Запуск через jsch
  • tail не создавал никакого вывода для jsch и, следовательно, к его выходному потоку.

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

( while true; do echo 'running';  sleep 5; done ) & ( tail -f --pid=$! log-file )

Ответ 10

Можно создать фон 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
)

Ответ 11

Не используйте хвост - вы можете получить тот же "монитор новейшей вещи в файле", используя read.

Здесь я использую FIFO вместо файла журнала:

function startServer() {
  mkfifo logfile
  startJavaprocess > logfile &

  a=""; while [ "$a" != "Started" ]; do read <logfile a; done

  echo "Server Started"
}

Обратите внимание, что это оставляет FIFO висящим вокруг.

Ответ 12

Это должно работать, и хвост должен умереть, как только субматерия будет умирать


function startServer() {
  touch logfile
  startJavaprocess &gt logfile &

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

Try this:

function startServer() {
  while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      return 0
    fi
  done &lt &lt(startJavaprocess | tee logfile)
}

Ответ 13

Использование tail -n0 -f, переданного в grep, действительно является хорошим решением, и действительно, первый процесс в трубе будет умирать, когда он пытается вывести на мертвый процесс grep.

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

Использование опции "-m1" в grep выглядит так, как будто бы делает именно то, что вы хотите, и оставляйте вход сразу после соответствующей строки, но, похоже, это не повлияло или не помогло мне в поиске похожих функциональность. Я подозреваю, что буфер буфера все еще содержит весь вывод текста из хвоста или какую-то другую причину для хвоста не иметь ничего, что можно было бы выводить. Вы хотели, чтобы этот текст после grep-match оставался для вывода следующим, потому что его то, что бы убило ваш хвост, когда он пытался (все еще рискованно - что происходит, если его последняя строка по какой-то причине?), И возвращает управление вызывающему script.

Я нашел один способ обойти это, чтобы вывести что-нибудь в конец файла журнала после выхода grep; то есть.

tail -f logfile | (grep -q; echo → logfile)

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

shopt -s huponexit был бы полезен, но для его подоболочки.

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

Ответ 14

Для исходного вопроса, почему команда exit не ушла, я получил ту же проблему и, наконец, нашел причину.

Используя режим отладки bash, я вижу, что вызывается команда exit, но процесс все еще висел до тех пор, пока еще одна строка не запустится в файл журнала сразу после "Начата". Самое странное, когда вышло "Начиналось", даже выезд был вызван, процесс все еще зацепился чем-то. Я думаю, что это было tail -f, пока не выйдет еще одна строка, это действительно освободит крючок.

Итак, если вы напечатаете еще одну строку после ее запуска, ваш монитор сразу выйдет.

Ответ 15

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

startServer (pid=101)
   startServer (pid=102) << This is the subshell created by using parens "(...)"
      tail -f logfile (pid=103) << Here the tail process
      startServer (pid=104)     << Here the logic that detects the end-marker

В этом подходе логика обнаружения конечного маркера (pid 104) ищет родительский PID (102) и все его дочерние элементы и убивает всю партию - включая ее. Тогда дедушка и бабушка (pid 101 выше) могут продолжить.

function startServer() {
  touch logfile
  startJavaprocess > logfile &

  tail -f logfile | while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      mypid=$BASHPID
      pipeParent=$(awk '/^PPid/ {print $2}' /proc/$mypid/status)
      kill -TERM $pipeParent $(pgrep -P $pipeParent)  # Kill the subshell and kids
    fi
  done
}

# To invoke startServer(), add a set of parens -- that puts it in a subshell:
(startServer())

Ответ 16

Используя комбинацию ответов, я придумал это простое решение. В этом примере вызывается Tomcat startup.sh script, а затем обрабатывает журнал catalina.out до тех пор, пока не запустится "Запуск сервера", а затем прекратит обработку.

#!/bin/bash

function logUntilStarted() {
    tail -n0 -F /home/tomcat/logs/catalina.out | while read line; do
        if echo $line && echo $line | grep -q 'Server startup' ; then
            pkill -9 -P $$ tail > /dev/null 2>&1
        fi
    done
}

/home/tomcat/bin/startup.sh
logUntilStarted

Ответ 17

tail -n0 --pid=$(($BASHPID+1)) -F logfile | sed -n '/Started/{s/.*/Server Started/p; q}'

При конвейеризации PID являются последовательными, поэтому pid хвоста будет $BASHPID, а pid sed будет $BASHPID + 1. Переключатель -pid приведет к выходу хвоста (правильно!), Когда команда sed завершит работу. Эта команда sed будет искать /Started/, а затем заменить всю строку (. *) На "Server Started", а затем выйти.