リモートからサーバーをテストし続けるシェルスクリプトを書き込もうとしたが、ログアウトするとelseステートメントに落ち続ける


9

サーバーをテストし続け、サーバーがダウンしたときにメールで通知するシェルスクリプトをここに記述しようとしています。

問題は、ssh接続からログアウトすると&、のようにコマンドの最後で実行したにもかかわらず./stest01.sh &、自動的に他の場所に分類され、再度ログインして強制終了するまで中断なくメールを送信し続けることです。

#!/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
私はbashのエキスパートではあり:ませんが、コロンは何をしますか?それはセミコロンだったと私には理にかなってい;ます...
Ned64

3
@ Ned64 :何もしません。これは、それが行うように設計されていることです。ここでは、テストを反転する代わりに、テストを使用してbeforeを実行しませんelse
Kusalananda

@Kusalananda OK、ありがとう。問題を説明できるタイプミスかもしれないと思った。
Ned64

1
また、ログアウト後にシェルスクリプトを実行したままにしようとする理由も混乱しています。これにはcronまたはsystemdタイマーの方が適していますか?
クリフアームストロング

回答:


20

GNU grepが結果を書き込もうとすると、SSH接続がなくなっているため、出力を書き込む場所がないため、ゼロ以外の終了ステータスで失敗します。

つまり、ifステートメントは常にelse分岐します。

これを説明するには(これはあなたのケースで正確に何が起こっているのかでgrepはありませんが、GNU が出力を書き込めない場合に何が起こるかを示しています):

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

ここでは生成grepする文字列を示していますechoが、grepどこにも書き込めないように両方の出力ストリームを閉じています。ご覧のとおり、GNUの終了ステータスはgrep0ではなく2です。

これはGNU grepに特有でありgrep、BSDシステムでは同じように動作しません。

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

これを修正するには、スクリプトが出力を生成しないことを確認してください。これはで行うことができますexec >/dev/null 2>&1。また、我々は、使用する必要がありgrep、そのと-qオプション我々は内のすべての興味ではないですので、見て(これは、一般的にもスピードアップするでしょう、それからの出力をgrep、それはファイル全体を解析する必要はありませんが、この場合には、それは非常に少し加えるとファイルが非常に小さいので速度の違い)。

要するに:

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

また、ping直接にテストを使用して、中間ファイルの1つを不要にすることもできます(実際には、日付スタンプのみを含む他の中間ファイルを取り除くこともできます)。

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

上記のスクリプトの両方のバリエーションでは、送信された電子メールの数を最小限に抑えるために、ホストに到達できなかった場合にループを終了することを選択しています。サーバーが最終的に再起動することが予想される場合はbreak、代わりに、たとえば、sleep 10mまたは何かで置き換えることができます。

また、で使用するオプションを少し調整しpingました-i 1が、であまり意味がありません-c 1

より短い(ホストに到達できない場合でもメールを送信し続けたい場合を除く):

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

毎分実行するcronジョブとして(サーバーがダウンし続けると、毎分メールを送信し続けます):

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

を使用>&-するとfdが閉じます(ファイル記述子1が閉じているなど)が、SSH接続を閉じると別の効果があります(ファイル記述子はまだ存在していますが、反対側のどこにも接続されていません)。まだ立っています。つまり、GNU grepは、出力を書き込もうとして失敗した場合にゼロ以外で終了します。ええ、最善の解決策は、pingの終了ステータスを直接確認することです。
filbranden

4
exec </dev/null >/dev/null 2>&1冒頭付近に追加することで、スクリプト全体のすべてを/ dev / nullに/ dev / nullからリダイレクトする方が安全な場合があります。そうすれば、たとえばpingstderrに何かを書き込むことを決定した場合、問題は発生しません。
Gordon Davisson

@GordonDavisson stdinを/dev/nullここからプルする理由は実際にはわかりませんが、出力を整理しました。提案をありがとう。
Kusalananda
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.