sedで複数行にまたがった文字列を置換する方法
はじめに
sed
コマンドで置換する場合、 複数行にまたがった文字列も置換対象としたい場合があります。
検証環境
$ uname -moi
x86_64 MacBookPro11,4 Darwin
$ bash -version | head -n 1
GNU bash, バージョン 5.0.11(1)-release (x86_64-apple-darwin18.6.0)
$ sed --version | head -n 1
sed (GNU sed) 4.2.2
置換対象の文字列が複数行にまたがる場合とは?
sed
はラインエディタの思想を受け継いでいるため、 基本的に入力情報を1行ずつ受け取って処理を行 います。
例えば、 %
で囲まれた部分を空文字に置換するといった場合、合致パターンが 1 行で収まるため容易に置換できます。
$ echo 'xx%aaa%yyy'
xx%aaa%yyy
$ echo 'xx%aaa%yyy' | sed 's/%.*%//'
xxyyy
ところが、 合致パターンが複数行にまたがったときにはうまく置換してくれません 。
# 2つの%の間に改行が含まれている
$ echo 'xxx%aaa
bbb%yyy'
xxx%aaa
bbb%yyy
# 置換されない
$ echo 'xxx%aaa
bbb%yyy' | sed 's/%.*%//'
xxx%aaa
bbb%yyy
2 つの対処法を取り上げます。
1.GNU 版 sed が利用できる場合
GNU 版の sed が利用できる場合、 -z
オプションを利用することで、簡単に対処できます。
これは sed
のデフォルトの挙動である「行単位の処理」から「ヌル文字単位の処理」に挙動を切り替える、というオプションになります。
注意しなければならないのは、GNU 版の sed でしか利用できないオプションである点です。Linux の場合は GNU 版の sed が導入されているディストリビューションが多いですが、Mac の場合にはプリインストールの sed では利用できません。GNU 版の sed を brew install
しなければなりません 。
どうしても GNU 版の sed を利用できない場合については後述します 。
通常のテキストにヌル文字が入っていることはありませんので、これによりテキスト全体を読み込み、一度に置換できます。
$ echo 'xxx%aaa
bbb%yyy' | sed -z 's/%.*%//'
xxxyyy
2.GNU 版 sed が利用できない場合
GNU 版の sed が利用できないケースも当然あります。
その場合には、以下のコマンドをお試しください。
# ちゃんと置換されていますね!
$ echo 'xxx%aaa
bbb%yyy' | sed ':l; N; s/%.*%//; b l;'
xxxyyy
sed
のコマンド部分のみ簡単に解説します。
今回取り上げたコマンドは ;
を区切り文字として 4 つの処理で構成されています。
:l
N
s/%.*%//
b l
それぞれのフェーズで行っている処理は以下のようになります。
:
は goto 文におけるラベル のようなものを設定します。ここではl
というラベルを設定していることになります。N
は、1 行データを読み込んでsed
のバッファ領域に蓄えます。sed
のバッファ領域をs
内部コマンドで置換します。- 置換ができた場合はバッファの内容が出力されます。
- 置換ができなかった場合はバッファの内容は据え置きされます。
b l
は、ラベルl
に戻る命令になります。
最終的に残ったバッファ領域の内容は、処理終了直前に出力されます。
ひとこと
sed -z
はかなり強力で、例えば「連続して並んだ空白行は 1 つにする」といった処理も簡単に実現できます。
# 空白行が多い
$ cat test.txt
android
Linux
windows
windows
Linux
# スッキリ!
$ sed -z 's/\n\{3,\}/\n\n/g' < test.txt
android
Linux
windows
windows
Linux
注意しないといけない点があります。
通常は行単位に実行される sed
を全文字列を一度に処理するため、メモリ負荷が増えます。
あまりに巨大なファイルを処理する場合にはメモリ不足の注意が必要です。
ディスカッション
コメント一覧
まだ、コメントがありません