Intentando escribir un script de shell que sigue probando un servidor de forma remota, pero sigue cayendo en la instrucción else cuando finalizo la sesión

9

Tratando aquí de escribir un script de shell que sigue probando mi servidor y enviarme un correo electrónico cuando se cae.

El problema es que cuando salgo de la conexión ssh, a pesar de ejecutarlo &al final del comando, como ./stest01.sh &, automáticamente cae en otra cosa y sigue enviándome correos ininterrumpidamente, hasta que vuelvo a iniciar sesión y lo mato.

#!/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
fuente
1
No soy un experto en bash, pero ¿qué hace el colon :? Para mí tendría sentido que fuera un punto y coma ;...
Ned64
3
@ Ned64 El :no hace nada. Para esto está diseñado. Aquí, en lugar de invertir la prueba, la usan antes para no realizar operaciones else.
Kusalananda
@Kusalananda OK, gracias. Pensé que podría ser un error tipográfico que podría explicar el problema.
Ned64
1
También estoy confundido por qué uno intentaría dejar un script de shell ejecutándose después de cerrar sesión. ¿No serían los temporizadores cron o systemd una mejor opción para esto?
Cliff Armstrong

Respuestas:

20

Cuando GNU grepintenta escribir su resultado, fallará con un estado de salida distinto de cero, porque no tiene donde escribir la salida, porque la conexión SSH se ha ido.

Esto significa que la ifdeclaración siempre toma la elserama.

Para ilustrar esto (esto no es exactamente lo que está sucediendo en su caso, pero muestra lo que sucede si GNU grepno puede escribir su salida):

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

Aquí estamos greppara la cadena que echoproduce, pero cerramos ambas secuencias de salida para grepque no pueda escribir en ningún lado. Como puede ver, el estado de salida de GNU grepes 2 en lugar de 0.

Esto es particular de GNU grep, grepen los sistemas BSD no se comportará igual:

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

Para remediar esto, asegúrese de que el script no genere resultados. Puedes hacer esto con exec >/dev/null 2>&1. Además, deberíamos usarlo grepcon su -qopción, ya que no estamos interesados ​​en ver el resultado (generalmente esto también aceleraría, grepya que no necesita analizar todo el archivo, pero en este caso hace muy poco) diferencia en velocidad ya que el archivo es muy pequeño).

En 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

También puede usar una prueba pingdirectamente, eliminando la necesidad de uno de los archivos intermedios (y también deshaciéndose del otro archivo intermedio que realmente solo contiene una marca de fecha):

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

En ambas variaciones de la secuencia de comandos anterior, elijo salir del ciclo en caso de que no llegue al host, solo para minimizar la cantidad de correos electrónicos enviados. En su lugar, podría reemplazar el breakcon eg sleep 10mo algo así si espera que el servidor finalmente vuelva a aparecer.

También he modificado ligeramente las opciones utilizadas, pingya -i 1que no tiene mucho sentido -c 1.

Más breve (a menos que desee que continúe enviando correos electrónicos cuando no se pueda acceder al host):

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

Como un trabajo cron que se ejecuta cada minuto (continuaría enviando correos electrónicos cada minuto si el servidor continúa inactivo):

* * * * * ping -q -c 1 -W 1 >/dev/null 2>&1 || ( date | mail -s "Server down" myemail@address.com )
Kusalananda
fuente
El uso >&-cerrará el fd (como en, el descriptor de archivo 1 está cerrado), mientras que cerrar la conexión SSH tendrá un efecto diferente (un descriptor de archivo seguirá existiendo, pero no conectado a nada en el otro lado). Creo que el punto sigue en pie, que es que GNU grep sale de un valor distinto de cero si intenta escribir una salida y eso falla. Sí, la mejor solución es verificar directamente el estado de salida del ping.
filbranden
44
Podría ser más seguro simplemente redirigir todo a / desde / dev / null para todo el script agregando exec </dev/null >/dev/null 2>&1cerca del comienzo. De esa manera, si, por ejemplo, pingdecide escribir algo para stderr, no causará ningún problema.
Gordon Davisson
@GordonDavisson Realmente no veo una razón para extraer stdin desde /dev/nullaquí, pero resolví la salida. Gracias por la sugerencia.
Kusalananda