2008年9月19日金曜日

sedを使ったテキスト書き換え処理を覚えよう (番外編)

今から2年前に、3回にわけて、sedのかんたんな使い方を紹介したことがありました。





過去記事









先日、sedを使うネタが1つ出てきたことがありまして、久々に、またsedの使い方について、少しだけ書いてみようと思います。



sedの「nコマンド」を使った実際の例です。





■ ADSLルータから、グローバルIPアドレスを教えてもらいたい



ADSLで、IPアドレスが動的に割り当てられる場合に(ADSLで接続するたびにIPアドレスが変化するような場合)、インターネット側から家のパソコンにアクセスできるようにしたいときは、

「ダイナミックDNS」

を利用するのは、定番のテクニック。これは、ADSLで接続したときに割り当てられたグローバルIPアドレスに、DNSにて、特定のホスト名(FQDN)を割り振ることで、インターネット側からは、割り振ったホスト名を使ってアクセスできるようにするものです。



IPアドレスが変化してしまっても、ダイナミックDNSで、ホスト名に割り当てられたIPアドレスを更新すれば、ふたたび、いつものホスト名を使って、インターネット側からアクセスできるようになります(更新が隅々まで行き届くまで、ちょっと時間がかかるかもしれませんが)。



ダイナミックDNSでIPアドレスを登録するときに、グローバルIPアドレスを調べなければなりません。グローバルIPアドレスはどうやって調べたらいいでしょうか?いくつか手段はあると思いますが、今回は、ADSLルータに教えてもらうことにしました。





先日、実家で、IP電話を導入したのですが、その際、ADSLルータも交換することになりました。交換後のADSLルータは、「Aterm WD701CV」というものです。



20080902



Aterm Support Information | WD701CV | トップページ
http://www.aterm.jp/eaccess/701/



ADSLルータは、たいてい、Webブラウザを使ってルータの管理ができる機能を持っています。WebブラウザでADSLルータにアクセスして、グローバルIPを知ることができます。





200809180



今回やりたいのは、



人がWebブラウザを操作することなく、スクリプトを実行するだけで、自動的に、グローバルIPアドレスを取得できるようにしよう



ということです。



wgetコマンドは、httpサーバにアクセスして、HTMLファイルをダウンロードしたりできるコマンドです。スクリプトで自動処理をしたいときに、とっても便利なコマンドです。余談ですが、curlというコマンドもあって、これはフォームのPOSTもできるので、かなりいろんなことが自在に出来ます。



今回やりたいことは、以下のようにして、実現できます。



  • wgetでADSLルータにアクセスして、グローバルIPアドレスが表示されている画面のHTMLソースをダウンロード


  • ダウンロードしたHTMLファイルを、sedで処理して、IPアドレスを抜き出す




いやぁ、前振りが長かった。長すぎた。





■ むむむ、これは・・・



以前使っていたADSLルータでは、わりと単純なsedの使い方で対応出来たんです。



こんな感じです。



#! /bin/sh



wget --quiet \
  --http-user 名前 \
  --http-passwd=パスワード \
  -O - \
  'http://192.168.1.1/cgi-bin/main.cgi?cc_webname=STATUS' | \
  sed -n -e '/ADSL IP/s|[^0-9]*\([0-9.]*\).*|\1|p'



これは、HTMLファイルの中で、「ADSL IP」という行を見つけたら、その行から、IPアドレスと思われる部分を、抽出しているだけです。

 

今回のADSLルータAterm WD701CVは、一筋縄ではいきませんでした。



IPアドレスが記述されている行に、なんの特徴も無かったからです。



該当部分の前後だけ、抜粋してみます。





200809181



<TR BGCOLOR=THISTLE>
    <TD><LABEL onMouseOver=msgShow(event,11) onMouseOut=msgHide()>WAN側 IPアドレス</LABEL></TD>
    <TD>555.666.777.888 </TD>
</TR>



<TR BGCOLOR=THISTLE>
    <TD><LABEL onMouseOver=msgShow(event,14) onMouseOut=msgHide()>WAN側 プライマリDNS</LABEL></TD>
    <TD>111.222.333.444 </TD>
</TR>







一応、グローバルIPアドレスの部分は「555.666.777.888」という伏字にしときましたが、「    <TD>555.666.777.888 </TD>」という行によく似た行が、LAN側アドレスや、DNSのアドレスなど、ほかにもいろいろあるので、この行がグローバルIPアドレスだ、と特定しづらいんです。



なんだか厄介な感じがしてきますが、これは、視点をちょっと変えて、sedの「nコマンド」を使うと、うまくいっちゃうんです。


■ まずは、グローバルIPアドレスが表示されているWebページのURLを調べましょうか



とりあえず、wgetでHTMLをダウンロードできるようにしないと、話ははじまりません。グローバルIPアドレスが表示されているWebページのURLを調べましょう。



なぜか、ADSLルータは、HTMLのフレーム機能を使っていることが多いのですが、フレームを使っていると、ブラウザのアドレスバーに表示されているURLと、ブラウザに表示されている内容とが、一致しないことがあります(アドレスバーに表示されていたURLにアクセスしても、別の画面表示になる)。



そういうときは、ショートカットメニューにたいていある「リンク先を新しいウインドウで表示する」というような名前の機能を使うと、本来フレーム内で表示されるはずだった部分が、独立したウインドウで表示されます。





200809185



もともとはフレームの中身として表示されていた部分が、独立して表示されます。





200809182



そして、アドレスバーにURLも表示されます。





200809184





うちの場合、以上のようにして調べたら



http://192.168.1.1/info_main.html



でした。



JavaScriptを使ってたり、フォームを使ってたりすると、上のテクニックは使えないので、ウェブブラウザの何らかの機能の手助けを借りるとか、目視でHTMLソースを解読するとか、パケットキャプチャするとか・・・、ちょっとめんどくさいことをやらないといけないですね。



ADSLルータではまだ見たことないですが、Refererを見てアクセスを制限してたり、cookieを使ってたり、とかなってたりすると、それはそれで厄介になりますが、話が発散してしまうので、やめときます。


■ wgetでダウンロードしてみる



wgetにこんな引数をつけて、ダウンロードできます。行は長くなるので、途中で折り返しています。



/usr/local/bin/wget --quiet
   --http-user ユーザー名
   --http-passwd パスワード
   -O -
   'http://192.168.1.1/info_main.html'



「-O -」は、ダウンロードした内容を、ファイルに書き出すのではなく、標準出力へそのまま書き出すためのオプションです。パイプでsedに渡せば、スマートに処理できます。



「--quiet」はログ出力をしないようにします。



その他のオプションは、見たまんま、です。



■ 今回は、sedのnコマンドを覚えよう



sedのnコマンドは、次の行を読み出します。今回はこれを使うと、うまくいくのです。



HTMLをもう一度確認してみます。

<TR BGCOLOR=THISTLE>
    <TD><LABEL onMouseOver=msgShow(event,11) onMouseOut=msgHide()>WAN側 IPアドレス</LABEL></TD>
    <TD>555.666.777.888 </TD>
</TR>

なお、ウェブブラウザ上では、長い行が自動的に折り返されて表示されているかもしれませんが、<TD><LABEL onMouseOver ~ </LABEL></TD>は、1行になってます。



HTMLファイルの内容を検索してみたら、「msgShow(event,11)」という文字列は、1回だけ出現していました。これは、検索条件に使えそうです。



こんな手順をsedで実行すれば、グローバルIPアドレス抽出ができます。



  1. 「msgShow(event,11)」という文字列を含む行を見つけたら


  2. nコマンドで次の行を読み込んで


  3. IPアドレスを抽出する






sedで正規表現を書く場合、検索パターンにカッコがついてるとエスケープする手間がかかるので、ちょこっと変えて



/event,11.*WAN.*IP/



というパターンでマッチングさせることにしました。



sedスクリプトとしては、以下のようになります。



/event,11.*WAN.*IP/{
        n
        s|[^0-9]*\([0-9.]*\).*|\1|p
        q
}



sは、置換コマンド。おなじみですね。



qは、そこで即座に処理を終了するコマンドです。グローバルIPアドレスを1個見つけたら、もうそれ以上処理を継続してもムダなので、とりあえずつけておきました





■ 完成したシェルスクリプト



こんな感じになりました。



#! /bin/sh



/usr/local/bin/wget --quiet \
    --http-user ユーザー名 \
    --http-passwd パスワード \
    -O - \
        'http://192.168.1.1/info_main.html' | \
        sed -n -e '/event,11.*WAN.*IP/{
        n
        s|[^0-9]*\([0-9.]*\).*|\1|p
        q
}'





これを実行すると、IPアドレスが標準出力に出力されます。


■ さいごに



sedの「nコマンド」を覚えると、sedでできる範囲がさらに広がりますねぇ。







2 件のコメント:

  1. まだISDNだったころにグローバルIPアドレスを取得する方法として、プロバイダのスペースにアクセスしてきたIPを返すっていうCGIを作ってグローバルIPをアプリに取り込んでました。
    あの頃は色々なものを作ってたなぁ。
    Webアプリ作りが楽しかった頃・・・。
    今は、何に使うの? ホントに使うの? って言葉で却下されますが、、。

    返信削除
  2. 実は使ってます。
    #! /bin/sh
    echo "Content-type: text/plain"
    echo ""
    echo $REMOTE_ADDR

    返信削除