Der Versuch, ein Shell-Skript zu schreiben, das einen Server weiterhin remote testet, aber beim Abmelden immer wieder in die else-Anweisung fällt

9

Ich versuche hier, ein Shell-Skript zu schreiben, das meinen Server ständig testet und mir eine E-Mail sendet, wenn er ausfällt.

Das Problem ist, dass wenn ich mich von einer SSH-Verbindung abmelde, obwohl ich sie &am Ende des Befehls ausführe ./stest01.sh &, sie automatisch in else fällt und mich ununterbrochen weiterschickt, bis ich mich erneut anmelde und sie beende.

#!/bin/bash
while true; do
    date > sdown.txt ;
    cp /dev/null pingop.txt ;
    ping -i 1 -c 1 -W 1 myserver.net > pingop.txt &
    sleep 1 ;
    if
        grep "64 bytes" pingop.txt ;
    then
        :
    else
        mutt -s "Server Down!" myemail@address.com < sdown.txt ;
        sleep 10 ;
    fi
done
Vasconcelos1914
quelle
1
Ich bin kein Bash-Experte, aber was macht der Doppelpunkt :? Es würde für mich Sinn machen, wenn es ein Semikolon wäre ;...
Ned64
3
@ Ned64 Der :macht nichts. Dafür ist es konzipiert. Anstatt den Test umzukehren, verwenden sie ihn, um zuvor ein No-Op durchzuführen else.
Kusalananda
@Kusalananda OK, danke. Ich dachte, es könnte ein Tippfehler sein, der das Problem erklären könnte.
Ned64
1
Ich bin auch verwirrt, warum man versuchen würde, ein Shell-Skript nach dem Abmelden laufen zu lassen. Wären Cron- oder Systemd-Timer dafür nicht die bessere Wahl?
Cliff Armstrong

Antworten:

20

Wenn GNU grepversucht, das Ergebnis zu schreiben, schlägt es mit einem Exit-Status ungleich Null fehl, da die Ausgabe nirgends geschrieben werden kann, da die SSH-Verbindung unterbrochen ist.

Dies bedeutet, dass die ifAnweisung immer den elseZweig übernimmt .

Um dies zu veranschaulichen (dies ist nicht genau das, was in Ihrem Fall passiert, aber es zeigt, was passiert, wenn GNU grepseine Ausgabe nicht schreiben kann):

$ echo 'hello' | grep hello >&- 2>&-
$ echo $?
2

Hier haben wir grepfür den String, der echoerzeugt, aber wir schließen beide Ausgabestreams für grep, damit er nirgendwo schreiben kann. Wie Sie sehen können, ist der Exit-Status von GNU grep2 statt 0.

Dies gilt insbesondere für GNU grep, da sich grepBSD-Systeme nicht gleich verhalten:

$ echo 'hello' | grep hello >&- 2>&-    # using BSD grep here
$ echo $?
0

Um dies zu beheben, stellen Sie sicher, dass das Skript keine Ausgabe generiert. Sie können dies mit tun exec >/dev/null 2>&1. Außerdem sollten wir verwenden sein grepmit seiner -qWahl , da wir in haupt nicht daran interessiert sind zu sehen , die Ausgabe von ihm (dies in der Regel auch die beschleunigen würde , grepda es nicht die ganze Datei zu analysieren braucht, aber in diesem Fall ist es macht sehr wenig Geschwindigkeitsunterschied, da die Datei so klein ist).

Zusamenfassend:

#!/bin/sh

# redirect all output not redirected elsewhere to /dev/null by default:
exec >/dev/null 2>&1

while true; do
    date >sdown.txt

    ping -c 1 -W 1 myserver.net >pingop.txt

    if ! grep -q "64 bytes" pingop.txt; then
        mutt -s "Server Down!" myemail@address.com <sdown.txt
        break
    fi

    sleep 10
done

Sie können einen Test auch pingdirekt für verwenden, um die Notwendigkeit einer der Zwischendateien zu beseitigen (und auch die andere Zwischendatei zu entfernen, die wirklich immer nur einen Datenstempel enthält):

#!/bin/sh

exec >/dev/null 2>&1

while true; do
    if ! ping -q -c 1 -W 1 myserver.net; then
        date | mutt -s "Server Down!" myemail@address.com
        break
    fi

    sleep 10
done

In beiden Varianten des obigen Skripts entscheide ich mich, die Schleife zu verlassen, wenn der Host nicht erreicht wird, um die Anzahl der gesendeten E-Mails zu minimieren. Sie können stattdessen das breakdurch zB sleep 10moder etwas ersetzen , wenn Sie erwarten, dass der Server irgendwann wieder hochfährt.

Ich habe auch die verwendeten Optionen leicht angepasst, pingda -i 1dies nicht viel Sinn macht -c 1.

Kürzere (es sei denn, Sie möchten, dass weiterhin E-Mails gesendet werden, wenn der Host nicht erreichbar ist):

#!/bin/sh

exec >/dev/null 2>&1

while ping -q -c 1 -W 1 myserver.net; do
    sleep 10
done

date | mutt -s "Server Down!" myemail@address.com

Als Cron-Job, der jede Minute ausgeführt wird (würde weiterhin jede Minute E-Mails senden, wenn der Server weiterhin ausfällt):

* * * * * ping -q -c 1 -W 1 >/dev/null 2>&1 || ( date | mail -s "Server down" myemail@address.com )
Kusalananda
quelle
Mit >&-wird das fd geschlossen (wie in, Dateideskriptor 1 ist geschlossen), während das Schließen der SSH-Verbindung einen anderen Effekt hat (ein Dateideskriptor ist immer noch vorhanden, aber mit nichts auf der anderen Seite verbunden). Ich denke, der Punkt steht immer noch, das heißt, dass GNU grep ungleich Null beendet wird, wenn es versucht, eine Ausgabe zu schreiben, und dies fehlschlägt. Ja, die beste Lösung besteht darin, den Exit-Status von Ping direkt zu überprüfen.
Filbranden
4
Es ist möglicherweise sicherer, einfach alles für das gesamte Skript nach / von / dev / null umzuleiten, indem Sie es exec </dev/null >/dev/null 2>&1am Anfang hinzufügen . Auf diese Weise pingverursacht es kein Problem , wenn z. B. beschlossen wird, etwas an stderr zu schreiben.
Gordon Davisson
@ GordonDavisson Ich sehe keinen Grund, stdin von /dev/nullhier zu ziehen , aber ich habe die Ausgabe aussortiert. Danke für den Vorschlag.
Kusalananda