it-swarm.com.de

Endet -f wurde in einem Shell-Skript gestartet

Ich habe folgendes.

  1. Ein Java-Prozess, der Protokolle in die Standardausgabe schreibt 
  2. Ein Shell-Skript, das den Java-Prozess startet 
  3. Ein weiteres Shell-Skript, das das vorherige ausführt und das Protokoll umleitet
  4. Ich überprüfe die Protokolldatei mit dem Befehl tail -f auf die Erfolgsmeldung.

Selbst wenn ich im Code Exit 0 habe, kann ich den tail -f-Prozess nicht beenden.

Das lässt mein Skript nicht enden. Gibt es eine andere Möglichkeit, dies in Bash zu tun?

Der Code sieht wie folgt aus.

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
}
24
rangalo

Die beste Antwort, die ich finden kann, ist diese

  1. Setzen Sie ein Zeitlimit für den Lesevorgang, tail -f logfile | read -t 30 line
  2. Beginnen Sie mit --pid=$$, um den Vorgang zu beenden, sobald der Bash-Prozess abgeschlossen ist.

Es werden alle Fälle abgedeckt, die mir einfallen (Server hängt ohne Ausgabe, Server-Exits, Server startet korrekt).

Vergessen Sie nicht, Ihren Schwanz vor dem Server zu starten.

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

der -F "liest" die Datei, auch wenn sie nicht existiert (beginnt mit dem Lesen, wenn sie erscheint). Der -n0 liest noch nichts in der Datei. Sie können also an die Protokolldatei anhängen, anstatt sie jedes Mal zu überschreiben, und an die Standardprotokollrotation.

BEARBEITEN:
Ok, also eine ziemlich grobe "Lösung", wenn Sie Schwanz verwenden. Es gibt wahrscheinlich bessere Lösungen mit etwas anderem als Schwanz, aber ich muss es Ihnen geben, der Schwanz holt Sie ganz schön aus dem gebrochenen Rohr heraus. Ein Abschlag, der mit SIGPIPE umgehen kann, würde wahrscheinlich besser funktionieren. Der Java-Prozess, der aktiv ein Dateisystem abbricht, mit einer Art "lebendiger" Nachricht, ist wahrscheinlich noch einfacher zu warten.

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
}
24
falstro

Basierend auf den Antworten, die ich hier gefunden habe, habe ich mir das ausgedacht. 

Es befasst sich direkt mit tail und tötet es, sobald wir die erforderliche Protokollausgabe gesehen haben. Die Verwendung von "pkill -P $$ tail" sollte sicherstellen, dass der richtige Prozess beendet wird.

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
}
7
David Resnick

Gemäß der tail-Manpage können Sie das Ende eines Prozesses beenden, nachdem ein Prozess stirbt

In BASH können Sie die PID des zuletzt gestarteten Hintergrundprozesses mit $! SO wenn Sie bash verwenden:

tail -f --pid=$! logfile
6
Robert Christie

Ich habe eine ähnliche Situation gehabt, in der ich ein Protokoll für eine "gestartete" Nachricht innerhalb einer angemessenen Zeitspanne bearbeiten muss, und wenn es während dieser Zeit nicht gefunden wird, muss ich es beenden. Hier ist, was ich am Ende getan habe.

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
}

Ich bin nicht sicher, ob dies sehr elegant ist oder sogar der beste Weg, es zu tun, aber es funktioniert gut genug für mich. Ich würde mich für alle Meinungen freuen :)

2
user985216

Erfassen Sie die PID des Hintergrundprozesses

pid=$!

Verwenden Sie die Option --pid = PID von tail, damit sie nach dem Abschluss des Prozesses mit $ PID beendet wird.

2

Es ist möglich, tail -f logfile im Hintergrund zu senden, tailpid an die while read-Loop-Subshell zu senden und eine trap bei EXIT zu implementieren, um den Befehl tail zu beenden.

( (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
)
1
nurro

Ich hatte das gleiche Problem, konnte keine einfache und gute Lösung finden. Ich bin nicht gut in Python, aber ich habe es irgendwie geschafft, das zu lösen:

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 __== "__main__":
    main()
1
user1008204

Anstatt den Prozess zu beenden, können Sie stattdessen die Prozess-ID des tail -f-Prozesses suchen und beenden (ein kill -9 wäre hier sogar sicher, wenn Sie sicher sind, dass die Protokolldatei abgeschlossen ist).

Auf diese Weise wird der while read line natürlich beendet und Sie müssen nicht beenden.

Oder, da Sie die tail nicht wirklich für die Ausgabe auf dem Bildschirm verwenden, können Sie auch die etwas ältere Schule ausprobieren:

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

Hatte ein ähnliches Problem, bei dem der Schwanzprozess nicht getötet wurde 

  1. Jsch durchlaufen 
  2. tail produzierte keine Ausgabe zu jsch und damit zu seinem Ausgabestrom.

Benutze den --pid=$!, um ihn zu töten und starte eine unendliche while-Schleife, um etwas im Hintergrund vor dem Schwanz zu wiederholen, der beim Töten des zugrunde liegenden Prozesses getötet wird und somit den Schwanz tötet.

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

Wie wäre es mit einer Endlosschleife anstelle der Befehlszeilenoption -f für tail?

function startServer() {
  startJavaprocess > logfile &

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

Die Verwendung von tail -n0 -f zu grep ist in der Tat eine schöne Lösung von Nizza, und der erste Prozess in der Pipe stirbt ab, wenn versucht wird, einen Dead Grep-Prozess auszugeben.

Wenn Sie jedoch nach Text suchen, der in der Nähe der letzten aktuellen Ausgabe des Endes erscheint, hat grep bereits die gesamte Eingabe aus dem Endpunkt (in einem Block) gelesen, und daher wird im Text keine weitere Textausgabe erfolgen log, das die Pipe als grep hinunterschicken muss, las es bereits, bevor es beendet wurde (oder war es vielleicht schon im Pufferspeicher) - das ist zumindest mein Verständnis.

Die Verwendung der Option "-m1" für grep sieht so aus, als würde sie genau das tun, was Sie möchten, und die Eingabe unmittelbar nach der übereinstimmenden Zeile belassen, aber es schien keinen Unterschied zu machen oder half mir bei der Suche nach ähnlichen Funktionen. Ich vermute, der Pufferspeicher enthält immer noch den gesamten Text von tail oder einen anderen Grund dafür, dass tail nichts mehr auszugeben hat. Sie wollten, dass dieser Post-grep-match-Text als nächstes noch ausgegeben wird, weil er Ihren Schwanz töten würde, wenn er es versuchte (immer noch riskant - was passiert, wenn es aus irgendeinem Grund die letzte Zeile ist?) Und die Kontrolle an das aufrufende Skript zurückgibt .

Ich fand eine Möglichkeit, etwas in das Ende der Protokolldatei auszugeben, sobald der grep beendet wurde; dh.

tail -f Logdatei | (grep -q; echo >> logfile)

Ich habe die Theorie, dass (wenn meine Vermutung richtig ist) Sie die Pipe zwingen könnten, weniger gepuffert zu werden, damit sie ohne diese Funktion zum Laufen kommt, oder dass Sie vielleicht einen Huponexit-Setzbefehl zur entsprechenden Pipe-Komponente hinzufügen - dh in (wahrscheinlich geschweiften) Klammern Hilfe; aber es war mir egal, eine leere Zeile an das Logfile anzuhängen, und es funktionierte in Ordnung und es war nur ein kleineres Testskript (also kein langlebiges Logfile, das an ein Format für andere Verarbeitung gebunden sein muss).

shopt -s huponexit wäre nützlich, aber für die Unterschale davon.

PS mein erster Beitrag hier, ich hätte es gerne als Kommentar zu einer vorhandenen Antwort getan, anstatt Dinge zu wiederholen, aber ich glaube nicht, dass ich das jetzt kann.

0
Breezer

Dies sollte funktionieren und der Schwanz sollte sterben, sobald die Sub-Shell stirbt


function startServer() {
  touch logfile
  startJavaprocess > logfile &

  while read line 
  do
    if echo $line | grep -q 'Started'; then
      echo 'Server Started'
      exit 0
    fi
  done < <(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 < <(startJavaprocess | tee logfile)
}
0
rcarson

Für die ursprüngliche Frage, warum der Exit-Befehl nicht beendet wurde, bekam ich das gleiche Problem und fand schließlich die Ursache.

Wenn Sie den Debug-Modus von bash verwenden, kann ich sehen, dass der exit-Befehl aufgerufen wurde, aber der Prozess hing immer noch, bis eine weitere Zeilenbereinigung in die Protokolldatei unmittelbar nach dem 'Started' erfolgte. Das Seltsame ist, als 'Started' herauskam, selbst wenn Exit aufgerufen wurde, der Prozess immer noch von etwas abhängig ist. Ich denke, es war tail -f, bis eine weitere Zeile herauskommt, wird der Haken wirklich losgelassen.

Wenn Sie also nach dem Start eine weitere Zeile drucken, wird der Monitor sofort beendet.

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

Beim Piping sind die PIDs sequentiell, daher ist die PID des Schwanzes $ BASHPID und die PID des Sed ist $ BASHPID + 1. Der --pid-Schalter bewirkt, dass der Schwanz (richtig!) Beendet wird, wenn der sed-Befehl beendet wird. Dieser sed-Befehl sucht nach/Started/und ersetzt dann die gesamte Zeile (. *) Durch "Server Started" und beendet dann.

0
Christian Herr

Führen Sie den vorherigen Befehl mit Nohup aus.

In meinem Fall Java-Jar mit Nohup ausführen, wie z

Nohup Java -jar trade.jar xx.jar &

es wird keine Protokollausgabe erstellt, es wird jedoch ein neues "Nohup.out" erstellt. Die ursprüngliche Protokolldatei trade.log funktioniert ebenfalls.

Dann zeigt tail -f trade.log die Shell Log-Informationen an, Strg-c kann sie unterbrechen und zu Shell zurückkehren. 

0
hatanooh

Meine bevorzugte Lösung für dieses Problem ist, den Befehl 'tail' und seinen Konsumenten in eine Subshell zu setzen und die Filterlogik die Eltern und ihre Kinder (einschließlich des Tail-Prozesses) töten zu lassen. Wenn Sie sich den Prozessbaum ansehen, wird es Folgendes sein:

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

Bei diesem Ansatz sucht die Endmarken-Erkennungslogik (PID 104) nach ihrer übergeordneten PID (102) und allen ihren untergeordneten Objekten und tötet den gesamten Stapel - einschließlich sich selbst. Dann können die Großeltern (PID 101 oben) fortfahren.

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())
0
Stabledog

Mit einer Kombination von Antworten kam ich zu dieser einfachen Lösung. In diesem Beispiel wird das Script start.sh von Tomcat aufgerufen. Anschließend wird das Protokoll catalina.out protokolliert, bis "Server-Start" protokolliert wird und das Tailing beendet wird.

#!/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
0
Matt

Verwenden Sie kein Tail - Sie können dasselbe "überwachen, was das Neueste in der Datei ist", indem Sie read verwenden.

Hier verwende ich anstelle der Protokolldatei ein FIFO :

function startServer() {
  mkfifo logfile
  startJavaprocess > logfile &

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

  echo "Server Started"
}

Beachten Sie, dass hier ein FIFO hängen bleibt.

0
Alex Brown