it-swarm.com.de

Wie kann ich Bashs verwenden, wenn Befehle gemeinsam getestet und gefunden werden?

Ich habe ein Verzeichnis mit Absturzprotokollen und möchte eine bedingte Anweisung in einem Bash-Skript verwenden, das auf einem Befehl find basiert.

Die Protokolldateien werden in diesem Format gespeichert:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

Ich möchte, dass die if-Anweisung nur dann true zurückgibt, wenn für eine bestimmte App ein Absturzprotokoll vorhanden ist, das in den letzten 5 Minuten geändert wurde. Der Befehl find, den ich verwenden würde, lautet:

find /var/log/crashes -name app-\*\.log -mmin -5

Ich bin mir nicht sicher, wie ich das richtig in eine if -Anweisung integrieren soll. Ich denke, das könnte funktionieren:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

Es gibt einige Bereiche, in denen ich unklar bin:

  • Ich habe mir das if flags angesehen, bin mir aber nicht sicher, welches ich verwenden soll, wenn überhaupt.
  • Benötige ich die Anweisung test oder sollte ich nur direkt gegen die Ergebnisse des Befehls find verarbeiten oder stattdessen find... | wc -l Verwenden, um eine Zeilenanzahl zu erhalten?
  • Nicht 100% notwendig, um diese Frage zu beantworten, aber test dient zum Testen gegen Rückkehrcodes, die Befehle zurückgeben? Und sie sind irgendwie unsichtbar - außerhalb von stdout/stderr? Ich habe die Seite man gelesen, bin mir aber noch ziemlich unklar, wann ich test verwenden und wie ich es debuggen soll.
26
cwd

[ Und test sind Synonyme (außer [ Erfordert ]), Daher möchten Sie [ test Nicht verwenden:

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

test gibt einen Exit-Status von Null zurück, wenn die Bedingung erfüllt ist, andernfalls ungleich Null. Dies kann tatsächlich durch ein beliebiges Programm ersetzt werden, um seinen Beendigungsstatus zu überprüfen, wobei 0 für Erfolg und ungleich Null für Fehler steht:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

Alle obigen Beispiele testen jedoch nur den Exit-Status des Programms und ignorieren die Ausgabe des Programms.

Für find müssen Sie testen, ob eine Ausgabe generiert wurde. -n Testet auf eine nicht leere Zeichenfolge:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

Eine vollständige Liste der Testargumente erhalten Sie, indem Sie help test In der Befehlszeile bash aufrufen.

Wenn Sie bash (und nicht sh) verwenden, können Sie [[ condition ]] Verwenden, das sich vorhersehbarer verhält, wenn Leerzeichen oder andere Sonderfälle in Ihrer Bedingung vorhanden sind. Ansonsten ist es im Allgemeinen dasselbe wie bei Verwendung von [ condition ]. Ich habe in diesem Beispiel [[ condition ]] Verwendet, wie ich es wann immer möglich getan habe.

Ich habe auch `command` In $(command) geändert, was sich im Allgemeinen ähnlich verhält, aber mit verschachtelten Befehlen besser ist.

28
mrb

find wird erfolgreich beendet, wenn keine Fehler aufgetreten sind. Sie können sich also nicht auf den Beendigungsstatus verlassen, um festzustellen, ob eine Datei gefunden wurde. Aber wie Sie sagten, können Sie zählen, wie viele Dateien gefunden wurden, und diese Nummer testen.

Es wäre ungefähr so:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test (auch bekannt als [) überprüft die Fehlercodes der Befehle nicht, hat eine spezielle Syntax zum Ausführen von Tests und wird dann mit einem Fehlercode von 0 beendet, wenn der Test erfolgreich war. oder 1 sonst. Es ist if, das den Fehlercode des Befehls überprüft, den Sie an ihn übergeben, und dessen Text basierend darauf ausführt.

Siehe man test (Oder help test, Wenn Sie bash verwenden) und help if (Dito).

In diesem Fall gibt wc -l Eine Zahl aus. Wir verwenden die Option -gt Von test, um zu testen, ob diese Zahl größer als 0 Ist. Wenn dies der Fall ist, wird test (oder [) Mit dem Exit-Code 0 Zurückgegeben. if interpretiert diesen Exit-Code als Erfolg und führt den Code in seinem Hauptteil aus.

9
angus

Das wäre

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

oder

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

Die Befehle test und [ … ] Sind genau gleichbedeutend. Der einzige Unterschied ist ihr Name und die Tatsache, dass [ Ein schließendes ] Als letztes Argument erfordert. Verwenden Sie wie immer doppelte Anführungszeichen um die Befehlsersetzung, da sonst die Ausgabe des Befehls find in Wörter unterteilt wird. Hier wird ein Syntaxfehler angezeigt, wenn mehr als eine übereinstimmende Datei vorhanden ist (und wenn vorhanden) sind keine Argumente, [ -n ] ist wahr, während Sie [ -n "" ] wollen, was falsch ist).

In ksh, bash und zsh, aber nicht in ash, können Sie auch [[ … ]] Verwenden, für das unterschiedliche Parsing-Regeln gelten: [ Ist ein gewöhnlicher Befehl, während [[ … ]] Ein anderes Parsing-Konstrukt ist . Sie brauchen keine doppelten Anführungszeichen in [[ … ]] (Obwohl sie nicht weh tun). Nach dem Befehl benötigen Sie noch ;.

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

Dies kann möglicherweise ineffizient sein: Wenn sich in /var/log/crashes Viele Dateien befinden, werden sie von find alle untersucht. Sie sollten die Suche stoppen, sobald eine Übereinstimmung gefunden wird oder kurz danach. Verwenden Sie mit GNU find (nicht eingebettetes Linux, Cygwin) die Primärseite -quit.

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

Bei anderen Systemen wird Pipe find in head eingefügt, um zumindest kurz nach dem ersten Match zu beenden (find stirbt an einer kaputten Pipe).

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

(Sie können head -c 1 Verwenden, wenn Ihr Befehl head dies unterstützt.)


Alternativ können Sie zsh verwenden.

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then

Das sollte funktionieren

if find /var/log/crashes -name 'app-\*\.log' -mmin -5 | read
then
  service myapp restart
fi
1
Steven Penny
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

ist hier eine geeignete Lösung.

-exec service myapp restart ';' bewirkt, dass find den Befehl aufruft, den Sie direkt ausführen möchten, anstatt dass die Shell irgendetwas interpretieren muss.

-quit bewirkt, dass find nach der Verarbeitung des Befehls beendet wird, wodurch verhindert wird, dass der Befehl erneut ausgeführt wird, wenn zufällig mehrere Dateien vorhanden sind, die den Kriterien entsprechen.

0
Jules