Próba napisania skryptu powłoki, który zdalnie testuje serwer, ale po wylogowaniu wciąż pojawia się instrukcja else

9

Próbuję tutaj napisać skrypt powłoki, który ciągle testuje mój serwer i wysyłać mi e-maile, gdy przestanie działać.

Problem polega na tym, że kiedy wylogowuję się z połączenia ssh, pomimo uruchomienia go &na końcu polecenia, na przykład ./stest01.sh &, automatycznie wpada w inne i nieprzerwanie wysyła mi pocztę, dopóki nie zaloguję się ponownie i nie zabiję go.

#!/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
źródło
1
Nie jestem ekspertem od bashów, ale co robi dwukropek :? Dla mnie miałoby to sens, że to był średnik ;...
Ned64,
3
@ Ned64 Nie :robi nic. Właśnie po to jest przeznaczony. Tutaj zamiast odwracać test, używają go do wcześniejszego braku operacji else.
Kusalananda
@Kusalananda OK, dziękuję. Pomyślałem, że może to być literówka, która może wyjaśnić problem.
Ned64,
1
Jestem również zdezorientowany, dlaczego ktoś próbowałby pozostawić skrypt powłoki uruchomiony po wylogowaniu. Czy cron lub systemowe liczniki czasu nie byłyby lepszym wyborem?
Cliff Armstrong,

Odpowiedzi:

20

Kiedy GNU greppróbuje zapisać swój wynik, zakończy się niepowodzeniem z niezerowym statusem wyjścia, ponieważ nie ma gdzie zapisać danych wyjściowych, ponieważ połączenie SSH zniknęło.

Oznacza to, że ifinstrukcja zawsze przyjmuje elsegałąź.

Aby to zilustrować (nie jest tak dokładnie w twoim przypadku, ale pokazuje, co się stanie, jeśli GNU grepnie będzie w stanie zapisać swoich danych wyjściowych):

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

Tutaj mamy grepdla ciągu, który echoprodukuje, ale zamykamy oba strumienie wyjściowe dla grep, aby nie mógł nigdzie pisać. Jak widać, status wyjścia GNU grepto 2 zamiast 0.

Dotyczy to zwłaszcza GNU grep, grepw systemach BSD nie będą zachowywać się tak samo:

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

Aby temu zaradzić, upewnij się, że skrypt nie generuje danych wyjściowych. Możesz to zrobić za pomocą exec >/dev/null 2>&1. Powinniśmy także korzystać grepz tej -qopcji, ponieważ wcale nie jesteśmy zainteresowani wyświetlaniem danych wyjściowych (generalnie przyspieszyłoby grepto, ponieważ nie trzeba analizować całego pliku, ale w tym przypadku robi to niewiele różnica prędkości, ponieważ plik jest tak mały).

W skrócie:

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

Możesz również użyć testu pingbezpośrednio, usuwając potrzebę jednego z plików pośrednich (a także pozbywając się drugiego pliku pośredniego, który tak naprawdę tylko zawiera znacznik danych):

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

W obu wariantach powyższego skryptu wybieram wyjście z pętli po nieosiągnięciu hosta, aby zminimalizować liczbę wysłanych wiadomości e-mail. Możesz zamiast tego zamienić na breaknp. sleep 10mLub coś, jeśli spodziewasz się, że serwer w końcu się pojawi.

Lekko poprawiłem również używane opcje, pingponieważ -i 1nie ma to większego sensu -c 1.

Krótszy (chyba że chcesz, aby nadal wysyłał e-maile, gdy host jest nieosiągalny):

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

Jako zadanie crona uruchamiane co minutę (kontynuowałoby wysyłanie wiadomości e-mail co minutę, jeśli serwer nadal nie działa):

* * * * * ping -q -c 1 -W 1 >/dev/null 2>&1 || ( date | mail -s "Server down" myemail@address.com )
Kusalananda
źródło
Użycie >&-spowoduje zamknięcie fd (jak w, deskryptor pliku 1 jest zamknięty), podczas gdy zamknięcie połączenia SSH będzie miało inny efekt (deskryptor pliku będzie nadal dostępny, ale nie będzie podłączony do niczego po drugiej stronie.) Myślę, że o to chodzi nadal stoi, to znaczy, że GNU grep kończy niezerowe, jeśli spróbuje zapisać dane wyjściowe, a to się nie powiedzie. Tak, najlepszym rozwiązaniem jest po prostu sprawdzenie statusu wyjścia ping bezpośrednio.
filbranden
4
Bezpieczniej jest po prostu przekierować wszystko do / z / dev / null dla całego skryptu, dodając exec </dev/null >/dev/null 2>&1blisko początku. W ten sposób, jeśli np. pingZdecyduje się napisać coś do stderr, nie spowoduje to problemu.
Gordon Davisson,
@GordonDavisson Naprawdę nie widzę powodu, aby wyciągać /dev/nullstąd standardowe wejście , ale uporządkowałem wynik. Dzieki za sugestie.
Kusalananda