---------- SED 教室 第九回 「ホールドスペース」 ---------- 前回までに紹介した命令だけで SED のスクリプトをすべて書く事が可能なの ですが、ほかに知っておくとスクリプトが短く書けるという命令があります。た とえば次の命令です。 y/文字列1/文字列2/ パターンスペース中の文字のうち、「文字列 1」に含まれる文字を、「文字列2」の同じ位 置にある文字に置き換える。「文字列1」と 「文字列2」は同じ長さでなければならない。 例えば「y/abc/XYZ/」という命令を実行すると、パターンスペース中の文字 「a」は「X」で、「b」は「Y」で、「c」は「Z」でそれぞれ置き換えられるわけ です。つまり、次の三つの置換命令の略記です。 s/a/X/g s/b/Y/g s/c/Z/g このように、命令「y」を使うと、「s」だけで書くときに比べてスクリプトを 短く書けます。他にも便利な命令がいくつかありますが、ちょっと複雑なスクリ プトを書くとき有効なのが、ホールドスペースです。 ホールドスペースはパターンスペースと同様、文字列を記憶しておける場所で す。パターンスペースには命令「N」などを使って複数行を記憶させる事が可能 ですから、本当はパターンスペース以外に記憶場所が無くても良いのです。でも、 ホールドスペースを使った方がスクリプトが簡単に書ける場合があります。たと えば次に紹介するスクリプトなどです。次のスクリプトがホールドスペースを使 わないで書くとどうなるか、考えてみるのも面白いでしょう。 さて、前回の SED 教室からスクリプトに行番号がつきましたが、実は次のス クリプトを使ってつけたのです。 <<< NUMBER.SED >>> --------------------------------------- 1 x 2 1s/.*/ / 3 H 4 y/ 0123456789/11234567890/ 5 G 6 s/.*\([^0]0*\)\n\(.*\)\n\(.*\)[^9]9*$/\3\1 \2/ 7 h 8 s/\( *[0-9]*\).*/\1/ 9 /^9*$/s/^/ / 10 x --------------------------------------- 行番号をつけるためには、数字を 1 増やす変換を行わなければなりません。 では、数字が 1 つづつ増えていく時、数字はどのように変化するのでしょうか。 たとえば「13」の次は「14」、「7599」の次は「7600」ですね。まず気づくのは 変化する桁と変化しない桁があるという事です。たとえば「13」と次の「14」を 比べると十の位の数字は変化しません。もう一つの特徴は、変化する桁は必ず数 字が一つ増える ( 9 の次は 0 ) という事です。‥‥ なんて書いていてもばか ばかしいほど当たり前の事ですね。:-) 変化する桁に関しては次の命令でうまく行きそうです。 y/ 0123456789/11234567890/ 空白を「1」に置換しているのは、「 9」の次が「10」だからです。 ところがこの命令を実行すると、変化しない行まで置換してしまいます。従っ て置換が及ばない場所に置換すべきでない行を保存しておく必要があります。 この保存するための場所がホールドスペースです。 ホールドスペースは、パターンスペースと同様文字列を保存しておける場所で すが、パターンスペースの内容が標準入力から文字列を読み込んだり、置換を実 行する事によって変化するのに対し、ホールドスペースはいままで紹介してきた 命令によっては変化しません。ホールドスペース関連の命令は次の通りです。 h パターンスペースの内容をホールドスペースに保存します。それまでの ホールドスペースの内容は失われます。hold の h です。 H パターンスペースの内容を、セグメントとしてホールドスペースに追加 します。すなわちホールドスペースの最後に改行コードとパターンスペー スの内容を付加します。「h」のセグメント版です。 g ホールドスペースの内容をパターンスペースに読み込みます。それまで のパターンスペースの内容は失われます。get の g です。 G ホールドスペースの内容を、セグメントとしてパターンスペースに追加 します。すなわちパターンスペースの最後に改行コードとホールドスペー スの内容を付加します。「g」のセグメント版です。 x パターンスペースの内容とホールドスペースの内容を交換します。 eXchange の x です。 さて、パターンスペースにある数字を 1 増やす方法について考えます。たと えばパターンスペースの内容が「12399」という数字であるとしましょう。この 場合変化しない桁は上位二桁の「12」です。この二桁だけホールドスペースに保 存しても良いのですが、めんどうなので :-) 「H」で全桁ホールドスペースに保 存します。ここで「h」の代わりに「H」を使う理由はホールドスペースの内容を 消したくないからです。この時点でのホールドスペースの内容は次のようになり ます。 「<ホールドスペースの元の内容><改行>12399」 次に「y/ 0123456789/11234567890/」でパターンスペースの各桁の数字を 1 増やします。すると「23400」になりますが、正しいのは下 3 桁だけです。つま り、下位桁から見て行って、最初の「0」でない桁までは正しくなります。(ちょ っと考えれば当たり前の事ですね。) 一方保存した数字「12399」の下 3 桁は不 要です。つまり、下位桁から見て行って、最初の「9」でない桁までが不要とな るわけです。 さて保存した数字をパターンスペースに持ってくるために命令「G」を実行し ます。するとパターンスペースの内容は次のようになります。 「23400<改行><ホールドスペースの元の内容><改行>12399」 「400」と「12」を組み合わせれば、「12399」の次の数字「12400」を得る事 が出来ます。「400」は、最初のセグメントの下位桁から見て行って最初の 0 で ない桁までですから、正規表現「.*\([^0]0*\)\n」を使います。つまり最初の 「.*」に不要な上位 2 桁がマッチし、必要な下位 3 桁を記憶します。 一方、「12」は、最後のセグメントの下位桁から見て行って最初の 9 でない 桁までを除いた物ですから、正規表現「\n\(.*\)[^9]9*$」を使います。つまり 最初の「.*」に必要な上位 2 桁がマッチするので、その 2 桁を記憶するのです。 従って、 s/.*\([^0]0*\)\n\(.*\)\n\(.*\)[^9]9*$/\3\1 \2/ を実行すると「\1」に「400」が、「\3」に「12」が記憶されますから、パタ ーンスペースの内容は次のようになります。 「12400 <ホールドスペースの元の内容>」 見事「12399」の次の数字「12400」を得る事が出来ました。こうしてみると <ホールドスペースの元の内容> というのは標準入力から読み込んだ行であれば 数字が行番号になって丸くおさまる事が分かります。標準入力から読み込んだ行 をホールドスペースに記憶するには行頭で命令「x」を実行すれば OK です。 以上をまとめると <<< NUMBER.SED >>> になるわけです。スクリプト二行目は 行番号の初期化で、行番号用に最初三桁確保されます。八行目の置換命令で、パ ターンスペースの内容を数字のみにします。また九行目は行番号が「999」にな ったとき桁を一つ増やすためのものです。 さて、このように SED で行番号をつけようとすると結構複雑なスクリプトを 書く事になるわけですが、ミドリノセロー氏による日本語対応 GNU 版 SED では 行番号を扱えるエスケープシーケンスが追加されている為、<<< NUMBER.SED >>> は次のように簡単に書けてしまいます。困ったものだ。^_^;) でも、数字を 1 増やすテクニックは色々なところで応用がききますから、憶えて損はないでしょ う。 <<< NUMBER2.SED >>> --------------------------------------- 1 s/^/ \L#/ 2 s/^ *\([0-9]*[0-9 ][0-9 ][0-9 ]\)#/\1 / --------------------------------------- 一行目の置換文字列中の「\L」は現在読み込んでいる標準入力の行番号を表し ます。二行目で行番号が 3 桁以上の場合は行頭の空白を削除します。 --- GCD03723 (Greatest Common Divisor:最大公約数)