- ベストアンサー
ping -cコマンドで指定した個数のパケットを送った後、停止しない
シェルスクリプトの最初にpingを実行して、それが成功したら処理を続行し、失敗したら処理を中止するようにしようとしています。 成功した場合は想定通り、処理を続行するのですが、pingが失敗すると、なぜか入力待ち状態になり、pingが終了しません。 具体的には次のようになります。 $ ping -c 1 192.168.1.2 PING 192.168.1.2 (192.168.1.2) from 192.168.1.3 : 56(84) bytes of data. From 192.168.1.3 icmp_seq=1 Destination Host Unreachable この後、入力待ち状態になり、シェルに戻りません。 しかも何を入力しても引き続き入力待ち状態になり、強制的に終了させるまでシェルに戻りません。 入力待ち状態になるということは何か入力を要求しているのだとは思うのですが、何を入力すればよいのかさっぱり分かりません。 ここへはいったい何を入力すれば良いのですか。 ひょっとしてpingのバグなのでしょうか。 pingが成功しようが失敗しようが、スクリプトが中断することなく、シェルに戻るようにしたいのですが、何か良い方法はありませんか。 ちなみにpingの引数にホスト名を指定した際、名前解決ができなかった場合は、次のようにpingが終了するため、真に通信ができていない場合に入力待ち状態になる模様です。 $ ping -c 1 aaa ping: unknown host aaa $
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
>なんと今回の実験で作成したのも/bin/shスクリプトなのです~。 /bin/shは、bashへのシンボリックリンクですなので、/bin/shはbashそのものです。 私の試した環境はLinuxではなく、その環境の/bin/shはashです。 ただ、No.4でFedora Core 5で確認した時はbashです。 話は変わり、以下はRed Hat Linux 7.3および9で実行した結果です。 -------8<-------8<-------8<------- ○Red Hat Linux 7.3 [lean@RedHat7 lean]$ rpm -qf `which ping` iputils-20020124-3 [lean@RedHat7 lean]$ ping -c 1 192.168.1.2 PING 192.168.1.2 (192.168.1.2) from 192.168.1.201 : 56(84) bytes of data. From 192.168.1.201 icmp_seq=1 Destination Host Unreachable -------8<-------8<-------8<------- ○Red Hat Linux 9 [lean@RedHat9 lean]$ rpm -qf `which ping` iputils-20020927-2 [lean@RedHat9 lean]$ ping -c 1 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. From 192.168.1.202 icmp_seq=1 Destination Host Unreachable --- 192.168.1.2 ping statistics --- 1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms [lean@RedHat9 lean]$ -------8<-------8<-------8<------- 7.3では「Destination Host Unreachable」を表示した後で止まり、9ではちゃんとプロンプトに戻ります。 7.3と9とで違うのは、pingが含まれるiputilsパッケージのバージョンです。 あと、システムコールのトレースを行ってみたのですが、 -------8<-------8<-------8<------- [root@RedHat7 root]# strace ping -c 1 192.168.1.2 execve("/bin/ping", ["ping", "-c", "1", "192.168.1.2"], [/* 21 vars */]) = 0 <途中、省略> write(1, "From 192.168.1.201 icmp_seq=1 De"..., 59From 192.168.1.201 icmp_seq=1 Destination Host Unreachable) = 59 setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 recvmsg(3, 0xbfffe8e0, 0) = -1 EAGAIN (Resource temporarily unavailable) setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 recvmsg(3, 0xbfffe8e0, 0) = -1 EAGAIN (Resource temporarily unavailable) setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 recvmsg(3, 0xbfffe8e0, 0) = -1 EAGAIN (Resource temporarily unavailable) setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 recvmsg(3, 0xbfffe8e0, 0) = -1 EAGAIN (Resource temporarily unavailable) setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 recvmsg(3, 0xbfffe8e0, 0) = -1 EAGAIN (Resource temporarily unavailable) setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 recvmsg(3, 0xbfffe8e0, 0) = -1 EAGAIN (Resource temporarily unavailable) setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 recvmsg(3, 0xbfffe8e0, 0) = -1 EAGAIN (Resource temporarily unavailable) setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0 <以下、永遠に続く> -------8<-------8<-------8<------- どうも「Destination Host Unreachable」を表示した後、ソケットからのメッセージの受け取りずっと続けているように見えます。 なので、これどちらかと言うとpingのバグではないかと。 現にこれより新しいバージョンのは正常に動作していますし。 対処は、この不具合の対処されたpingをインストールすればいいのだけれど、Red Hat Linux 8はRedHatもFedora Legacy Projedctのサポートはもうないから、パッケージで探すのはちょっと難しいかな? あとは、Red Hat Linux 9あたりのiputilsのソースパッケージを持ってきて自分でビルドするとか。 今あるpingコマンドでどうにかして逃げるなら、-wでコマンドを指定秒数後終了させられるので、このオプションを使用して見るとか。
その他の回答 (5)
- a-saitoh
- ベストアンサー率30% (524/1722)
>& はもともと csh系の書き方で、shでは通用しません。が、bashでは拡張されていて、受け付けてくれるんですね。 ANo.5で指摘されているようにpingのトラブルのようですが、、、。とりあえず動かすなら、 ping .. >& /dev/null </dev/null としてみてください。本当にstdinの入力待ちになっているのなら、これで止らなくなります。パケット到着待ちか何かになっているなら、 </dev/null しても挙動は変わらないはずです。
お礼
a-saitohさん、ご返信ありがとうございます。 なるほど>&は環境により使えたり使えなかったりするのですね。 リナックスではbashが/bin/shを包含しているようですが、/bin/shスクリプトを作成する場合は>&を使用してはいけませんね。 >&は分かりやすくて好きだったので残念です。 ping .. >& /dev/null </dev/nullの方法を試したのですが、挙動は変わりませんでした。 ユーザーから何か入力を待っているわけではなく、やはりパケットの到着待ちか何かなのでしょうかね。 ひとまず、このスクリプトではNo.5のLeanさんに教えてもらったpingの-wオプションを使用して回避することにしました。
- Lean
- ベストアンサー率72% (435/603)
No.2です。 >if ping -c 1 192.168.1.2 &>/dev/null; then bashだと「&>word」という書き方が「>word 1>&2」と同一なのですね。 私が使用しているsh(/bin/sh)はbashではないので「&>word」で見事にバックグランドで実行され戻り値は全て0になっていたので気が付きませんでした。 ちなみにFedora Core 5で通信不可の状態のpingを実行してみましたが、ちゃんとプロンプトに戻ります。 [user@FedoraCore5 ~]$ ping -c 1 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. From 192.168.1.202 icmp_seq=1 Destination Host Unreachable --- 192.168.1.2 ping statistics --- 1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms [user@FedoraCore5 ~]$ echo $? 1 [user@FedoraCore5 ~]$ どのディストリビューションのどのリリースをお使いなのでしょうか?
お礼
Leanさん、ご返信ありがとうございます。 シェルはデフォルトのbashのままですが、スクリプトを作成するときは/bin/shを使用するようにしています。 なんと今回の実験で作成したのも/bin/shスクリプトなのです~。 にもかかわらず、Leanさんと私の環境では異なる結果になってしまうのですね~。 正確には結果は同じですが、私の環境の場合はなぜか途中で入力待ち状態になり、そこでCtrl-Cしないと先に進まないというまか不思議なことになっています。 念のため/bin/shでLeanさんと同様の操作を行ってみました。 [user@host user]$ /bin/sh sh-2.05b$ ping -c 1 192.168.1.2 PING 192.168.1.2 (192.168.1.2) from 192.168.1.3 : 56(84) bytes of data. From 192.168.1.3 icmp_seq=1 Destination Host Unreachable --- 192.168.1.2 ping statistics --- 1 packets transmitted, 0 received, +1 errors, 100% loss, time 0ms sh-2.05b$ ご覧ください、表示内容はLeanさんのものとほぼ同様です。 ただし、こちらは5行目の空行のところでCtrl-Cを押して6行目以降が表示されてシェルに戻ります。 むむむ、同一のシェルを使用して、同様の操作を行ったにも関わらず、挙動が相違するとは!! どうも迷宮入りしそうな質問をしてしまったようです。 ディストリビューションは最も利用者の多いと思われるRed Hat Linuxの8.0を使用しています。 Leanさん、他にも何か助言がありましたら、ぜひともお願いします。
- a-saitoh
- ベストアンサー率30% (524/1722)
if ping -c 1 192.168.1.2 &>/dev/null; then これって、/bin/shスクリプトですよね。 リダイレクションの文法を間違ってますね。 これだとpingがバックグラウンドに回ります。入力待ちに思える挙動は、pingの終了(タイムアウト)待ちでは? 標準出力と標準エラーを両方/dev/nullに捨てたいのだと思いますが、正しくはこうなります。 ping -c 1 192.168.1.2 >/dev/null 2>&1
補足
a-saitohさん、再返信ありがとうございます。 記載するのが遅れましたが作成しているスクリプトは、a-saitohさんのお察しのとおり/bin/shスクリプトです。 なんと私のリダイレクションの文法が間違っているのですか!? &>を使用すると標準出力と標準エラーを同一ファイルにリダイレクトできるのではないのですか!? 以前は>ファイル名 2>&1のように記述していたのですが、この記述方法はなんだか分かりづらいので&>を知ってからはずっとこちらを使用していたのです。 次のスクリプトを作成しました。 #!/bin/sh if ping -c 1 192.168.1.2 >/dev/null 2>&1; then : else echo 失敗 fi しかし実行結果は、やはり私のスクリプトと同様でした。 スクリプトを実行すると、すぐに入力待ち状態になってしまいます。 ここでCtrl-Cを押して強制的にpingを終了すると次の処理へ進みます。 すなわち、"失敗"を表示してシェルに戻ります。 これを何とか入力待ち状態にすることなく次の処理へ進むようにしたいのですが、何か良い方法はないでしょうか。 pingが何の入力を要求しているのかが分かれば、パイプコマンドであらかじめその値を渡しておき解決できる気がするのです。 また、&>を使用すると本当にバックグラウンドになってしまうのか次のように実験してみました。 $ ping -c 1 192.168.1.2 & [1] 4483 $ PING 192.168.1.2 (192.168.1.2) from 192.168.1.3 : 56(84) bytes of data. From 192.168.1.3 icmp_seq=1 Destination Host Unreachable $ ps | grep ping 4483 pts/1 00:00:00 ping $ kill -1 4483 $ ping -c 1 192.168.1.2 &>/dev/null [1]+ Hangup ping -c 1 192.168.1.2 $ ps | grep ping $ まず始めにpingをバックグラウンドで実行します(1~5行目)。 5行目の空行はなぜか入力待ち状態になるところです。 Ctrl-Cで強制的にシェルに戻った後、ps | grep pingでpingのプロセスが残存したままになっていることを確認しました(6~7行目)。 kill -1 4483で残存状態のpingプロセスを強制終了させます(8行目)。 次はpingの結果を、&>を使用してリダイレクトしました(9行目)。 リダイレクトしていますので何も表示しませんが、同様に入力待ち状態となりました。 同じくCtrl-Cを押すと、その時点でpingプロセスが終了します(10~11)。 この実験より、 コマンドの後に&を記述→バックグラウンドで実行 コマンドの後に&>ファイル名を記述→標準出力と標準エラーをファイルへ出力 ということが分かりました。 ですので私のリダイレクションの文法は正しいのではないでしょうか。 長くなりましたが、引き続き助言よろしくお願いします~。
- Lean
- ベストアンサー率72% (435/603)
>if ping -c 1 192.168.1.2 &>/dev/null; then pingコマンドをバックグランドで実行しているように見えるのですが、これだと常に戻り値が0になるのでエラーメッセージを表示する方には絶対行かないと思いますが。。。
お礼
Leanさん、ご返信ありがとうございます。 常に戻り値が0になるか実験するため、次のシェルスクリプトを実行してみました。 #!/bin/sh ping -c 1 cat &>/dev/null echo "$?:成功" ping -c 1 mouse &>/dev/null echo "$?:通信不可" ping -c 1 dog &>/dev/null echo "$?:名前解決失敗" これはpingの引数にcat、mouse、dogの3ホストを指定したときの、それぞれの戻り値を表示するスクリプトです。 結果表示を分かりやすくするため、戻り値の後に結果の予想を記述しました。 これを実行すると $ ./test 0:成功 1:通信不可 2:名前解決失敗 $ と表示します。 catはローカルホストです。→ping成功 mouseは停止しています。→ping失敗 dogはネームサーバーに未登録です。→名前解決失敗 このように私の予想どおりの表示が得られました。 よって&>を使用してリダイレクションを行ったとしても戻り値が常に0になるということはなく、通常のpingと同様の値を返すようです。 ただし、上記の結果表示をもっと詳細に説明すると次のようになります。 $ ./test 0:成功 ここで入力待ち状態となりCtrl-Cで強制的にpingを終了させて $ ./test 0:成功 1:通信不可 2:名前解決失敗 $ 最終的にこのように表示されます。 戻り値が0のときと2のときは入力待ち状態にはならないのですが、1のときはなぜか入力待ち状態となって次の行に行かないのです。 この原因が何だか分かりますでしょうか。 ところで&>を使用すると本当にバックグラウンドになるのか実験してみます。 この実験結果はNo.3のa-saitohさんのお礼欄に記載する予定ですので、こちらもご覧いただけますか。
- a-saitoh
- ベストアンサー率30% (524/1722)
シェルスクリプトの当該部分を書き込んでくれませんか。
補足
a-saitohさんご返信ありがとうございます。 シェルスクリプト中に次のif文を記述しました。 if ping -c 1 192.168.1.2 &>/dev/null; then : else echo サーバーと通信できないため処理を中止します >&2 exit 1 fi これによりサーバーから応答があった場合は次の処理へ進み、そうでない場合は、エラーメッセージを表示してシェルへ戻ると思っていたのですが、うまくいかないのです。 応答があった場合は問題なく次の処理へ進むのですが、pingがエラーになった場合はなぜか入力待ち状態となって、しかも何行入力しても、永遠と入力待ちになっているように思えます。 このときpingは何の入力を要求しているのでしょうか。 それが分かればパイプをうまく利用して、スクリプトを中断させずにシェルに戻れる気がするのです。
お礼
Leanさん、ご返信ありがとうございます。 いろんな環境をお持ちなのですね! 7.3でダメで9でできるということは、やはりバグの可能性が高いですね。 しかし9ですでに修正されているということは私が第一発見者になれず残念です! バグを承知で使い続けるというのは不本意ですが、今回はお教えいただいた-wで逃げることにします。 まだ詳細に調べたわけではないのですが、この-wオプションはpingが成功しているときは指定した秒数で終了するのですが、 今回のようにpingが失敗する場合は-w 10と指定しようが-w 10000と指定しようが、関係なく一定時間後に終了するようです。 まあ、今回の要件は入力待ち状態にならなければ良いので、この-wオプションを採用することにしました。 ところで/bin/shを見てみたら、確かにbashへのシンボリックリンクでした! すなわちリナックスでいう/bin/shというのはbashのことだったのですね。 ただbashを使用中/bin/shを起動するとプロンプトが変わるので、両者はやはり別物なのでは?とも思いました。 とにかく裏技っぽい手法ではありますが、問題が解決して嬉しいです。 どうもありがとうございました。