経緯
『1日1問、半年以内に習得 シェル・ワンライナー160本』(以下、ワンライナー本)を初めて問題8まで解いたが、sed, grep, awk, xargsといった基本的なコマンド操作に時間がかかり、取り組むのが負担になってきているので、ここらで『新しいLinuxの教科書』(以下、教科書)を使用して上記4コマンドの復習をする。
私がすでに理解している部分や、練習問題の問題文は端折るので、興味がある方は最後の『使用した本』の本文をご参照ください。
まずはsedから。
練習
まずワンライナー本の基本問題をもう一度やり直す。
練習1.3.a sedによる置換の練習
小問3
$ echo クロロメチルメチルエーテル | sed 's/メ/エ/' クロロエチルメチルエーテル $ echo クロロメチルメチルエーテル | sed 's/メ/エ/g' クロロエチルエチルエーテル
- 「sed 's/置換前/置換後/'」。最後にgをつけると該当する全てを置き換える(逆にいうと、gがなければ該当する最初の1つだけしか置き換えない)。
小問4
「&」は、検索対象の文字を再利用したいときに使用できる。以下では、「&」は検索対象の「エチル」を指す。
$ echo クロロエチルエーテル | sed 's/エチル/&&/' クロロエチルエチルエーテル
小問5
$ echo クロロメチルエチルエーテル | sed -E 's/(メチル)(エチル)/\2\1/' クロロエチルメチルエーテル
- 検索対象の文字を()で囲むと順番に番号が与えられ、置換後の文字列で「\1」「\2」という形式で呼び出せる。このような文字列の再利用機能を「後方参照」という。
- 「-E」は拡張正規表現を使うという宣言。(「-r」でも同じ)
小問5別解
$ echo クロロメチルエチルエーテル | sed -E 's/(メ..)(...)/\2\1/' クロロエチルメチルエーテル
- 「.」は任意の1文字を指す。「メ.....」で「最初がメでその後に任意の5文字が続く文字列」になる。これを2つの()で括って分けて置換後の文字列を作る。
2つ目の()部分を(エ..)にしても同様の結果が得られる。
$ echo クロロメチルエチルエーテル | sed -E 's/(メ..)(エ..)/\2\1/' クロロエチルメチルエーテル
sedコマンドの概要
練習問題で大体sedがどんな感じか思い出せたので、それらを踏まえて教科書のP238-247を読んで特徴を捉え直す*1。
概略
- 編集結果は標準出力に出力されるため、元のファイルは変更されない。
- 編集対象ファイルの指定がない場合は標準入力から読み込む。
行を表示するp
例えば以下のようなtest.txtに対して、1行目を表示させる。
$ cat test.txt A B C $ sed 1p test.txt A A B C
実行すると、1行目が2回連続で表示された後、2行目から最終行目までも表示される。
これは、sedコマンドは行を読み込むとまずパターンスペースという場所にいったんコピーし、そのパターンスペースに対して編集コマンドを実行*4してから、最後にそのパターンスペースの内容*5を出力するため。
パターンスペースを出力させないためには、「-n」オプションを追加する。これはワンライナー本の問題1の別解2で出てきたやつだ!仕組みがわかってスッキリ。
行を置換する
よく使われるsコマンド。
形式
s/置換前文字列/置換後文字列/フラグ
- フラグ : 省略可能。
- ファイル名 : 概略の形式にも書いたけど、ファイルを指定する場合はこの後ろに書く。
sedでの正規表現
sedでは正規表現は基本正規表現として解釈される。
上記の小問5でも出てきたが、拡張正規表現を使用したい場合は、Eもしくはrオプションをつける。
ただし、Linuxのsed(GNU sed)では、拡張正規表現の前にエスケープシーケンス(\記号)をつけることで基本正規表現として利用することができる。
例として小問5を基本正規表現で書くと以下のようになる。
$ echo クロロメチルエチルエーテル | sed 's/\(メチル\)\(エチル\)/\2\1/' クロロエチルメチルエーテル
わからなくはないが見づらい。
アドレス指定
dコマンドやpコマンドと同様にsコマンドでもアドレス指定ができる。
例として、ファイルの1行目から3行目までを置換対象としたい場合。
$ sed '1, 3s/置換前文字列/置換後文字列/g' sample.txt
最終行まで置換対象としたいときは「1, $s」でも「s」でも良い。
区切り文字の変更
ここまでsの区切り文字に/が使われてきたが、実はsの後ろの文字が自動的に区切り文字とみなされるため、どんな文字でも良い。ワンライナー本の問題3の別解にも出てきた。
例として、「/home」という文字列を「../」に置き換えたい場合。
$ sed 's!/home!../!g' sample.sh
感想
先にワンライナー本で数問問題を解いたからこそ、教科書の説明が頭に入ってきた。
例えば、フィルタとして動作しているから、編集対象ファイルを指定しなければ標準入力から読み込まれるとか、元のファイルは変更しないとかは、最初に説明読んでいても実感できずに読み流していたと思う。
今まで定義を知ってから問題を解くという流れでやってきたけど、まず問題を解いてみてから定義を振り返る、の繰り返しの方が自分に合っているかもしれない。