テキストファイル「list.txt」の各行に、IPアドレスがひとつずつ書かれており、これらに対して順にtracerouteコマンドを実行し、実行結果を標準出力に出力するシェルスクリプトは、次のようになると思います。
#!/bin/sh
while read line
do
traceroute $line
done < list.txt
ここで、list.txtに書かれているアドレス群は、到達できないことが最初からわかっているとします。
つまり、tracerouteを実行すると、途中から
<hop数> * * *
と出力されることが、あらかじめわかっています。
ただし、何hop目からこうなるかは、わかっていません。
このため、「* * *」が出力された時点で、そのtracerouteの実行を止め、次の行のIPアドレスに対するtracerouteの実行を始めさせたいと考えています。
ただし、どういう経路をたどったのかを知る必要があるため、途中経過はひきつづき標準出力に出力させたいです。
これをシェルスクリプトでうまく実現する方法を教えてください。
できればshかbashでの実装がよいです。
ちょっと微妙だけど、一応書けました。
回答に対するコメントで書いても良いのですが、こっちのほうがシンタックスハイライトが効いて見やすくなるので、敢えてもう一回、回答の方に書きます。
#!/bin/sh while read target do if [ -z $target ]; then continue fi traceroute $target | while read line do echo "$line" echo "$line" | grep '\* \* \*' >/dev/null if [ $? -eq 0 ]; then exit fi done done <$1
微妙なのは、「* * *」を検出してから traceroute が停止するまでです。
明示的に traceroute を止めているのではなくて、traceroute の出力がパイプでつながっている先のプロセス(この場合、「while read line ~」以下がサブプロセスとして実行される)が終了することで、結果、traceroute も終了、という動きを期待しています。
で、本当にそんなので traceroute が終了するか、というのですが、単独で traceroute が MAX TTL に達するよりも短い時間で、次の traceroute が実行されているので、おそらく、途中で traceroute が終了していると思います。
あと、動かしてみて感じたのですが、バッファリングの関係で、画面表示はちょっとぎごちない感じになり、「* * *」を見つけたらスパっと止まる感じにはなりませんでした。
コメントにしようか回答にしようか迷ったのですが、とりあえず回答へ。
trouceroute で「*」が表示されるのは多くの場合、ルータから戻ってっくると期待している ICMP Echo Reply(traceroute が送信するパケットが UDP の場合は ICMP Port Unreachabe)が届かないためです。
tracert / traceroute の仕組み TCP/IP入門
なので「*」が表示されることと、目的のホストに到達できない、という事は一致しません。経路中のルータの設定が ICMP Echo Request を無視するような設定がされている場合、最終的なホストには到達するんだけど、「*」が表示されるケースがあります。また、経路中の FireWall で ICMP が破棄されているような場合、通常の TCP でのコネクションは OK だけど、traceroute だと、traceroute の最大 TTL(通常は 30)で到達できないように見える(ルータからの応答が無いのと、ホストからの応答が無いことを区別できない)、という事もあります。
ということで、「到達できないホストがあった時に、次の対象に進むために「*」の出力を拾う」という事自体が、目的に合致するのか、という疑問があります。
単に「*」が出力される時に待たされる時間が嫌だ、というのであれば、trouceroute のコマンドラインオプションで、通常、3回のパケット送信をするところを1回にしたり、通常、5秒の応答時間を3秒にしたり、という事はできます。
trouceroute -q1 -w3 www.example.com
書き方が良くなかったんですかね・・・
> 「*」が表示されることと、目的のホストに到達できない、という事は一致しません。
これは承知しています。
自分が知りたい情報が、最初に「*」が表示されるようになるまでに、どういう経路をたどるのかということなので、最大TTLまで待ちたくないのです。
なるほど。そういう意味だったんですね。
>list.txtに書かれているアドレス群は、到達できないことが最初からわかっているとします。
と書かれていたんで、そもそも、一覧の中に到達できないホストの IP アドレスが含まれていて、その到達できない相手の場合に、時間を無駄にしたくない、という話だと思ってしまいました。
> 自分が知りたい情報が、最初に「*」が表示されるようになるまでに、
> どういう経路をたどるのかということなので、最大TTLまで待ちたく
> ないのです。
となると、例えば、Firewall のようにフィルタリングするルータがあって、それより内側にあるルータはきちんと ICMP Echo Reply を返す、という状況で、内側のネットワークの経路状態を確認したい、ということでしょうか? 経路上に応答を返さないルータがあると、そのルータでは「*」だけど、次のホップではきちんと応答が返ってくる、という事もあるので、「最初の『*』」というのには、なにか理由があるのかな、と。
とりあえず、一本のシェルスクリプトで実現するは難しい気がします。シェルスクリプトから traceroute をバックグラウンドで起動した上で、trraceroute 標準出力をスクリプト自身の標準入力にリダレクトし、標準出力に結果を表示しつつ、文字判定で引っかかった、traceroute のプロセスを kill する、という事になると思うのですが、今のところ、ちょっとうまい方法が思いつきません。traceroute の出力を読み取るスクリプトと、それを監視するスクリプトの2本立てなら、できなくも無いかなぁ、と思うのですが、私の実力では、ぱっと「これでいけます」というのは提示できません。申し訳ありません。
ちょっと微妙だけど、一応書けました。
回答に対するコメントで書いても良いのですが、こっちのほうがシンタックスハイライトが効いて見やすくなるので、敢えてもう一回、回答の方に書きます。
#!/bin/sh while read target do if [ -z $target ]; then continue fi traceroute $target | while read line do echo "$line" echo "$line" | grep '\* \* \*' >/dev/null if [ $? -eq 0 ]; then exit fi done done <$1
微妙なのは、「* * *」を検出してから traceroute が停止するまでです。
明示的に traceroute を止めているのではなくて、traceroute の出力がパイプでつながっている先のプロセス(この場合、「while read line ~」以下がサブプロセスとして実行される)が終了することで、結果、traceroute も終了、という動きを期待しています。
で、本当にそんなので traceroute が終了するか、というのですが、単独で traceroute が MAX TTL に達するよりも短い時間で、次の traceroute が実行されているので、おそらく、途中で traceroute が終了していると思います。
あと、動かしてみて感じたのですが、バッファリングの関係で、画面表示はちょっとぎごちない感じになり、「* * *」を見つけたらスパっと止まる感じにはなりませんでした。
ありがとうございます。
直接終了させるわけではなくて、出力先を止めてしまうのですね。
たしかにすぐに止まるわけではないですが、Max TTLまで待つよりは先に止まるので、要件には合ってます。
ありがとうございます。
2013/10/11 00:10:03直接終了させるわけではなくて、出力先を止めてしまうのですね。
たしかにすぐに止まるわけではないですが、Max TTLまで待つよりは先に止まるので、要件には合ってます。