Tentando escrever um script de shell que continua testando um servidor remotamente, mas continua caindo na instrução else quando eu saio

9

Tentando escrever um script de shell que continua testando meu servidor e me enviando um e-mail quando ele ficar inativo.

O problema é que, quando eu saio da conexão ssh, apesar de executá-la &no final do comando ./stest01.sh &, ele automaticamente cai em outro lugar e continua me enviando ininterruptamente, até que eu faça o logon novamente e o mate.

#!/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
fonte
1
Eu não sou um especialista em festança, mas o que o cólon :faz? Não faria sentido para mim que fosse um ponto e vírgula ;...
Ned64
3
@ Ned64 O :não faz nada. É para isso que ele foi projetado para fazer. Aqui, em vez de inverter o teste, eles o usam para fazer uma operação não antes else.
Kusalananda
@Kusalananda OK, obrigado. Achei que poderia ser um erro de digitação que poderia explicar o problema.
Ned64
1
Também estou confuso por que alguém tentaria deixar um script de shell em execução após o logout. Os temporizadores cron ou systemd não seriam uma escolha melhor para isso?
Cliff Armstrong

Respostas:

20

Quando o GNU greptenta gravar seu resultado, ele falhará com um status de saída diferente de zero, porque não tem onde gravar a saída, porque a conexão SSH se foi.

Isso significa que a ifdeclaração está sempre assumindo o elseramo.

Para ilustrar isso (isso não é exatamente o que está acontecendo no seu caso, mas mostra o que acontece se o GNU grepnão conseguir gravar sua saída):

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

Aqui, procuramos grepa string que echoproduz, mas fechamos os dois fluxos de saída para grepque ele não possa gravar em nenhum lugar. Como você pode ver, o status de saída do GNU grepé 2 em vez de 0.

Isso é particular do GNU grep, grepnos sistemas BSD não se comportará da mesma maneira:

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

Para remediar isso, verifique se o script não gera saída. Você pode fazer isso com exec >/dev/null 2>&1. Além disso, devemos usar grepcom essa -qopção, pois não estamos interessados ​​em ver a saída dele (isso geralmente também acelera o processo grep, pois não precisa analisar o arquivo inteiro, mas, nesse caso, faz muito pouco diferença de velocidade, já que o arquivo é muito pequeno).

Em resumo:

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

Você também pode usar um teste pingdiretamente, removendo a necessidade de um dos arquivos intermediários (e também se livrando do outro arquivo intermediário que realmente contém apenas um carimbo de data):

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

Nas duas variações do script acima, optei por sair do loop quando não alcançava o host, apenas para minimizar o número de emails enviados. Em vez disso, você pode substituir breakpor, por exemplo, sleep 10mou algo assim, se espera que o servidor volte a aparecer novamente.

Também ajustei levemente as opções usadas, pingpois -i 1não faz muito sentido -c 1.

Menor (a menos que você queira continuar enviando e-mails quando o host estiver inacessível):

#!/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 um trabalho cron executado a cada minuto (continuaria enviando e-mails a cada minuto se o servidor continuar inativo):

* * * * * ping -q -c 1 -W 1 >/dev/null 2>&1 || ( date | mail -s "Server down" myemail@address.com )
Kusalananda
fonte
O uso >&-fechará o fd (como em, o descritor de arquivo 1 está fechado), enquanto o fechamento da conexão SSH terá um efeito diferente (um descritor de arquivo ainda estará presente, mas não conectado a nada do outro lado.) Acho que o ponto ainda permanece, o GNU grep sai diferente de zero se tentar gravar a saída e isso falhar. Sim, a melhor solução é apenas verificar o status de saída do ping diretamente.
filbranden
4
Pode ser mais seguro redirecionar tudo para / de / dev / null para todo o script, adicionando exec </dev/null >/dev/null 2>&1próximo ao início. Dessa forma, se, por exemplo, pingdecidir escrever algo para stderr, isso não causará problemas.
Gordon Davisson
@GordonDavisson Eu realmente não vejo um motivo para puxar o stdin /dev/nulldaqui, mas resolvi a saída. Obrigado pela sugestão.
Kusalananda