Beenden Sie das Programm, nachdem es eine bestimmte Zeile aus einem Shell-Skript ausgegeben hat

16

Hintergrund:

Ich schreibe ein Testskript für eine Computational Biology-Software. Die Ausführung der Software, die ich teste, kann Tage oder sogar Wochen dauern, sodass bei Systemabstürzen oder Stromausfällen eine Wiederherstellungsfunktion integriert ist.

Ich versuche herauszufinden, wie das Wiederherstellungssystem getestet werden kann. Insbesondere kann ich keine Möglichkeit finden, das Programm auf kontrollierte Weise "zum Absturz zu bringen". Ich dachte daran, eine SIGKILL-Anweisung so zu planen, dass sie nach einiger Zeit ausgeführt wird. Dies ist wahrscheinlich nicht ideal, da nicht garantiert wird, dass der Testfall jedes Mal dieselbe Geschwindigkeit ausführt (er wird in einer gemeinsam genutzten Umgebung ausgeführt), sodass ein Vergleich der Protokolle mit der gewünschten Ausgabe schwierig wäre.

Diese Software druckt für jeden abgeschlossenen Analyseabschnitt eine Zeile.

Frage:

Ich habe mich gefragt, ob es eine gute / elegante Möglichkeit (in einem Shell-Skript) gibt, die Ausgabe eines Programms zu erfassen und das Programm dann zu beenden, wenn eine bestimmte Zeile / Anzahl von Zeilen vom Programm ausgegeben wird.

Paul
quelle
1
Warum müssen Sie das Programm zu einem bestimmten Zeitpunkt stoppen? Aufgrund der Ausgabepufferung kann es vorkommen, dass Sie das Programm lange nach der Ausgabe des gesuchten Textes beenden.
Stardt
@stardt Ich wollte das Programm zu einem bestimmten Zeitpunkt stoppen, damit die Testergebnisse kontrollierter und reproduzierbarer sind. Ich habe jetzt einen Weg gefunden, dies zu umgehen. Ich werde das Programm nur einmal manuell beenden und dann nur den Wiederherstellungscode testen. Alle Wiederherstellungsversuche beginnen am selben Punkt. Ich teste, wie es sich erholt, nicht wie es doch abstürzt :)
Paul

Antworten:

7

Ein Wrapper-Skript wie dieses ist ein Standardansatz. Das Skript führt das Programm im Hintergrund aus und wiederholt dann die Protokolldatei jede Minute auf Zeichenfolgen. Wenn die Zeichenfolge gefunden wird, wird das Hintergrundprogramm beendet und das Skript beendet.

command="prog -foo -whatever"
log="prog.log"
match="this is the what i want to match"

$command > "$log" 2>&1 &
pid=$!

while sleep 60
do
    if fgrep --quiet "$match" "$log"
    then
        kill $pid
        exit 0
    fi
done
Kyle Jones
quelle
15

Wenn Ihr Programm auf SIGPIPE beendet wird (dies ist die Standardaktion), sollte es ausreichen, die Ausgabe in einen Reader zu leiten, der beim Lesen dieser Zeile beendet wird.

So könnte es so einfach sein wie

$ program | sed -e '/Suitable text from the line/q'

Wenn Sie die Standardausgabe unterdrücken möchten, verwenden Sie

$ program | sed -n -e '/Suitable text from the line/q'

Ebenso könnte man, wenn man nach einer bestimmten Anzahl von Zeilen anhalten möchte, den Kopf anstelle von sed verwenden, z

$ program | head -n$NUMBER_OF_LINES_TO_STOP_AFTER

Der genaue Zeitpunkt der , an dem die Abtötung tritt nicht auf dem Pufferung Verhalten des Terminals wie stardt schlägt vor , in den Kommentaren ab.

dmckee --- Ex-Moderator Kätzchen
quelle
-1. Hier gibt es einen schwerwiegenden Fehler. Das Programm wird nur beendet, wenn (wenn) es versucht, nach dem sedBeenden in die (defekte) Pipe zu schreiben . OP sagt "Diese Software druckt eine Zeile für jeden Abschnitt der Analyse, den sie abschließt". Dies kann bedeuten, dass das Programm einen zusätzlichen Abschnitt abschließt! Proof of Concept : { echo 1; sleep 3; >&2 echo peekaboo; sleep 3; echo 2; } | sed -e '/1/q'. Siehe diese Antwort . Mögliche Problemumgehung : ( program & ) | sed -e '/Suitable text from the line/q'; killall program. Machen Sie Ihre Antwort auf den Fehler aufmerksam und ich werde meine Ablehnung widerrufen.
Kamil Maciorowski
Hmmm ... in der Tat. Und das Pufferungsproblem macht es trotz Ihrer Verbesserung unzuverlässig (obwohl dies natürlich jede Lösung betrifft, die ich hier sehe). Ich nutze die Signalinfrastruktur gerne, wenn es möglich ist, aber irgendwann verliert sie an Eleganz. Vielleicht ist es besser, einfach das Ganze zu verschrotten.
dmckee --- Ex-Moderator Kätzchen
7

Alternativ zur Antwort von dmckee kann auch der grepBefehl mit der -mOption (siehe z. B. diese Manpage ) verwendet werden:

compbio | grep -m 1 "Text to match"

zu stoppen, wenn 1 Zeile gefunden wird, die mit dem Text übereinstimmt oder

compbio | grep -v -m 10 "Text to match"

auf 10 Zeilen warten, die nicht mit dem angegebenen Text übereinstimmen.

stardt
quelle
3

Sie können damit die expect(1)Standardausgabe eines Programms beobachten und dann vordefinierte Aktionen für einige Muster ausführen.

#!/usr/bin/env expect

spawn your-program -foo -bar 
expect "I want this text" { close }

Oder ein Einzeiler zum Einbetten in ein anderes Skript:

expect -c "spawn your-program -foo -bar; expect \"I want this text\" { close }"

Beachten Sie, dass der expectBefehl nicht standardmäßig mit jedem Betriebssystem geliefert wird. Möglicherweise müssen Sie es installieren.

Jamesits
quelle
Das ist eine schöne Lösung. Ich empfehle zu erwähnen set timeout -1, dass ein unbestimmtes Zeitlimit festgelegt werden soll, da sonst alles unter expectdem Standardzeitlimit von 10 Sekunden geschlossen wird.
Pepper_chico
1

Sie könnten eine "while" -Anweisung verwenden:

Sie müssen die Ausgabe Ihrer Software (oder ihrer Protokolle) weiterleiten und dann für jede neue Zeile einen bedingten Test durchführen und dann kill -KILL ausführen. Zum Beispiel (ich habe mit / var / log / messages als Protokolldatei getestet):

tail -f /var/log/messages | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

Oder direkt mit der Software:

./biosoftware | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

Sie müssen den Protokollpfad / Anwendungsnamen, die übereinstimmende Zeile und die zu tötende PID ersetzen (Sie können auch killall verwenden).

Viel Glück mit Ihrer Software,

Hugo,

Pistache
quelle