---------- SED 教室 第六回 「セグメント」 ---------- セグメントって何? 左官屋さんが塗っているものですね。(それはセメント) 前回までの講義で、標準入力から読み込んだ 1 行を SED でいかに料理できる かおわかり頂けたと思います。でも 1 行だけではなく複数行をまとめて加工し たいときがあります。例えば次のような Nifty の利用時間リストを作りたいと します。 LOG IN LOG OUT 利用時間 91/12/06 02:12:34 ~ 91/12/06 02:17:10 : 04分36秒 91/12/07 14:12:59 ~ 91/12/07 14:17:34 : 04分35秒 91/12/07 15:23:55 ~ 91/12/07 15:30:10 : 06分15秒 91/12/07 18:58:22 ~ 91/12/07 19:03:03 : 04分41秒 91/12/08 23:46:45 ~ 91/12/08 23:53:42 : 06分57秒 91/12/09 18:51:36 ~ 91/12/09 18:55:21 : 03分45秒 この様な表があると、12/7 は何回もアクセスしている、何故かな? あ、土曜 日か。とか、一回あたりの利用時間は似たようなものでだいたい 5 分程度か、 などが分かって便利です。 この表は Nifty が LOG OUT 時に表示する > LOG IN --- 91/12/06 02:12:34 > LOG OUT --- 91/12/06 02:17:10 > > ご利用時間は、 04分36秒でした。 を加工すれば作ることが出来そうですが、Nifty は LOG IN/OUT 時間と利用時 間を同じ行に表示してくれませんので、この 4 行を一行にまとめる必要があり ます。まとめてしまえば後は置換命令「s」で不要な文字を置き換えてしまえば 良いので簡単ですね。というわけで複数行を扱うための命令の紹介です。 N 標準入力から一行読み、パターンスペースに追加します。この命令を実 行することにより、標準入力の複数行の内容をパターンスペースに記憶す ることが出来るのです。この時パターンスペース内の複数行は改行コード によって区切られています。そして改行コードによって区切られたそれぞ れの文字列を「セグメント」と呼びます。 P パターンスペースの最初のセグメントと改行を標準出力に吐き出します。 要するに命令「p」のセグメント版です。 D パターンスペースの最初のセグメントを削除し、スクリプトの先頭へジ ャンプします。ただし削除後パターンスペースが空になった場合は標準入 力から次の行をパターンスペースに読み込みます。 つまりこの命令は、パターンスペースにセグメントが一つしかないとき (つまり、パターンスペースに改行コードが含まれていない時) は、命令 「d」と同じ動作をします。 たとえばパターンスペースの内容が「現在の行」という文字列の時、命令「N」 を実行して標準入力から「次の行」という行を読み込んだ場合、パターンスペー スの内容は「現在の行<改行>次の行」になります。この時次に命令「p」を実行 すると標準出力に ---------------------------------------- 現在の行<改行> 次の行<改行> ---------------------------------------- と吐き出されます。ところが「P」を実行すると出力されるのは ---------------------------------------- 現在の行<改行> ---------------------------------------- だけです。また、命令「D」を実行すると最初のセグメントが削除され、パタ ーンスペースの内容は「次の行」になります。セグメントを区切る改行コードも 一緒に削除されることに注意してください。削除後スクリプトの先頭にジャンプ します。パターンスペースにはまだセグメントが一つ残っているので標準入力か らの読み込みは行われません。 では早速この命令を使ってみます。Nifty の利用時間のリストを出力するスク リプトです。 <<< TIME.SED >>> ---------------------------------------- 1i\ LOG IN LOG OUT 利用時間 /^[ ]*LOG IN[ ]*-/{ N N N s/\n/ /g s/LOG OUT/~/ s/ご利用時間は、/:/ s/でした。// s/[A-Z-]*//g s/[  ][  ]*/ /g p } d ---------------------------------------- SED は起動されるとまず標準入力から 1 行読み込んでパターンスペースに記 憶し、スクリプトの一行目を見るのでしたね。実行条件は「1」、現在読み込ん でいるのは標準入力の 1 行目ですから条件成立。命令「i」を実行します。 命令「i」は、今まで紹介した命令と異なり、二行にわたっています。一行目 の行末に「\」がある事に注意してください。 i\ 文字列 「文字列」を標準出力に吐き出します。空白, タブを行頭に出 力する時と、改行を出力する時は、それらの文字の前に「\」をつ けます。 たとえば ---------------------------------------- i\ \ \ この行の最初には空白がある。\ \ この行の最初にはタブがある ---------------------------------------- という命令を実行した場合を考えます。この命令は四行にわたっていますが、 二行目と三行目の行末には「\」がありますね。つまり二行目と三行目の終わり の改行は、命令の終わりを意味する改行ではなく、命令「i」によって出力され る文字列の中に含まれる改行文字なのです。したがって実行しますと、標準出力 に次のような文字列が吐き出されます。 ---------------------------------------- <改行> この行の最初には空白がある。<改行> この行の最初にはタブがある<改行> ---------------------------------------- 同様にして命令「s」の置換文字列に改行コードを入れる事もできます。 たとえば ---------------------------------------- s/改行/\ / ---------------------------------------- はパターンスペース内の「改行」という文字を改行コードに置き換えます。 さて、スクリプトの一~二行目は ---------------------------------------- 1i\ \ LOG IN LOG OUT 利用時間 ---------------------------------------- ですから、最初に空白文字が出力され次に「LOG IN …」が出力されるわけで す。 さてスクリプトの一~二行目が済みましたから次は三行目です。 三行目は「/^[ ]*LOG IN[ ]*-/{」ですから、パターンスペースの内容が正規 表現「^[ ]*LOG IN[ ]*-」とマッチするときのみ「{」と「}」で囲まれた命令 が実行されるのでした。マッチしない場合は「}」の次の行まで一気に飛んで、 命令「d」を実行します。つまり、パターンスペースの内容を削除し、標準入力 から次の行をパターンスペースに読み込んで、スクリプトの先頭へジャンプしま す。つまり、スクリプトの三行目の正規表現にマッチするまで SED はひたすら 標準入力を 1 行づつ読み続けるのです。ご苦労様。 ではスクリプト三行目の正規表現は、どんな場合にマッチするのでしょうか。 えーと、パターンスペースの先頭 (「^」) から 0 個以上の空白 (「[ ]*」) が あってその次に文字列「LOG IN」、また 0 個以上の空白があって次に文字「-」、 残りは何でも良い。従って Nifty の LOG OUT 時に表示される > LOG IN --- 91/12/06 02:12:34 の行を、SED が読み込んだ時に成立します。 さてマッチする行が見つかるとスクリプトの四行目以降を実行することになり ます。まず紹介したばかりの命令「N」です。三回続けて「N」を実行します。す るとパターンスペースは「 LOG IN --- 91/12/06 02:12:34<改行> LOG OUT --- 91/12/06 02:17:10<改行><改行> ご利用時間は、  04分36秒でした。」になります。長いので見にくいのですが、要するにこれで Nifty が LOG OUT 時に表示する LOG IN/OUT の時間と利用時間の表示を行って いる 4 行がすべてまな板 (パターンスペース) の上にのったわけです。あとは 置換命令「s」で料理するだけです。 最初の置換命令は「s/\n/ /g」です。見慣れない正規表現が出てきました。 \n パターンスペース内の改行文字とマッチする正規表現。 したがって、パターンスペース内の改行文字 (「\n」) を全て (「g」) 空白 で置換するわけです。すでに説明したように、改行コードはパターンスペースで セグメントを区切るために使われていました。それ故この置換によって 4 つあ ったセグメントは 1 つのセグメントに合併してしまうのです。つまり、パター ンスペースの内容は「 LOG IN --- 91/12/06 02:12:34 LOG OUT --- 91/12/06 02:17:10  ご利用時間は、 04分36秒でした。」 になります。 残りの置換命令はすでに説明済みのものばかりですから説明は不要でしょう。 最後の置換命令「s/[  ][  ]*/ /g」の「[」と「]」で囲まれている文字は、 全角と半角の空白文字です。したがってこの命令は 1 個以上の全角または半角 の空白文字を 1 個の半角の空白文字で置き換えます。 そして置換が全部済んだ後、命令「p」で出来た料理 (パターンスペース) を 標準出力に出して一丁あがり、最後の命令「d」で後かたづけをして次の行を標 準入力から読み、スクリプトの先頭へジャンプ、ということになります。 いかがでしたでしょうか。ちょっと複雑なスクリプトでも一つづつ命令を追っ て行けば意外と簡単ですね。 いままでに説明した命令だけでかなり複雑な事を SED にやらせる事が出来る と思いますので、みなさんもなにかスクリプトを書いてみてはいかがでしょう。 次回は SED の最強の機能を紹介したいと思います。乞うご期待。 --- GCD03723 (Greatest Common Divisor:最大公約数)