Sto provando a scrivere uno script di shell che continua a testare un server in remoto, ma continua a cadere nell'istruzione else quando esco


9

Prova qui a scrivere uno script di shell che continua a testare il mio server e inviarmi un'e-mail quando diventa inattivo.

Il problema è che quando esco dalla connessione ssh, nonostante lo esegua &alla fine del comando, come ./stest01.sh &, cade automaticamente in altro e continua a spedirmi ininterrottamente, fino a quando non eseguo nuovamente l'accesso e lo uccido.

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

1
Non sono un esperto di bash, ma cosa fanno i due punti :? Per me avrebbe senso che fosse un punto e virgola ;...
Ned64,

3
@ Ned64 The :non fa nulla. Questo è ciò per cui è progettato. Qui, invece di invertire il test, lo usano per fare una no-op prima else.
Kusalananda

@Kusalananda OK, grazie. Ho pensato che potrebbe essere un errore di battitura che potrebbe spiegare il problema.
Ned64,

1
Sono anche confuso perché si dovrebbe provare a lasciare uno script di shell in esecuzione dopo il logout. I timer cron o systemd non sarebbero la scelta migliore per questo?
Cliff Armstrong,

Risposte:


20

Quando GNU greptenta di scrivere il suo risultato, fallirà con uno stato di uscita diverso da zero, perché non ha un posto dove scrivere l'output, perché la connessione SSH è sparita.

Ciò significa che la ifdichiarazione prende sempre il elseramo.

Per illustrare questo (questo non è esattamente ciò che sta accadendo nel tuo caso, ma mostra cosa succede se GNU grepnon è in grado di scrivere il suo output):

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

Qui siamo grepper la stringa che echoproduce, ma chiudiamo entrambi i flussi di output in grepmodo che non possano scrivere da nessuna parte. Come puoi vedere, lo stato di uscita di GNU grepè 2 anziché 0.

Questo è particolare per GNU grep, grepsui sistemi BSD non si comporterà lo stesso:

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

Per ovviare a questo, assicurarsi che lo script non generi output. Puoi farlo con exec >/dev/null 2>&1. Inoltre, dovremmo utilizzare grepcon la sua -qopzione poiché non siamo affatto interessati a vedere l'output da esso (questo generalmente accelererebbe anche grepperché non ha bisogno di analizzare l'intero file, ma in questo caso rende molto poco differenza di velocità poiché il file è così piccolo).

In breve:

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

Puoi anche usare un test pingdirettamente, rimuovendo la necessità di uno dei file intermedi (e anche liberandoti dell'altro file intermedio che in realtà contiene sempre e solo un datestamp):

#!/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 entrambe le varianti dello script sopra, scelgo di uscire dal loop in caso di mancato raggiungimento dell'host, solo per ridurre al minimo il numero di email inviate. Si potrebbe invece sostituire breakcon eg sleep 10mo qualcosa del genere se si prevede che il server alla fine ritorni.

Ho anche leggermente modificato le opzioni utilizzate con pingpoiché -i 1non ha molto senso -c 1.

Più breve (a meno che tu non voglia continuare a inviare e-mail quando l'host è irraggiungibile):

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

Come cron job in esecuzione ogni minuto (continuerebbe a inviare e-mail ogni minuto se il server continua a non essere attivo):

* * * * * ping -q -c 1 -W 1 >/dev/null 2>&1 || ( date | mail -s "Server down" myemail@address.com )

L'utilizzo >&-chiuderà il file fd (come in, il descrittore di file 1 è chiuso), mentre la chiusura della connessione SSH avrà un effetto diverso (un descrittore di file sarà ancora in giro, ma non collegato a nulla dall'altra parte.) Penso che il punto è ancora valido, ovvero GNU grep esce da zero se prova a scrivere output e ciò fallisce. Sì, la migliore soluzione è solo controllare direttamente lo stato di uscita del ping.
filbranden,

4
Potrebbe essere più sicuro reindirizzare tutto su / da / dev / null per l'intero script aggiungendo exec </dev/null >/dev/null 2>&1vicino all'inizio. In questo modo, ad esempio, se pingdecide di scrivere qualcosa su Stderr, non causerà problemi.
Gordon Davisson,

@GordonDavisson Non vedo davvero un motivo per estrarre lo stdin da /dev/nullqui, ma ho risolto l'output. Grazie per il suggerimento
Kusalananda
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.