sedで複数の改行+文字列を置換

sedで複数行置換を行うにはNを使えば良いが、1行目の検索文字列が続けて複数ある場合は少し工夫が必要になる。たとえば、

(改行)
(改行)
(改行)
文字列

のようなケースだ。

sedだけで置換

改行+文字列にマッチさせたい場合は、ループを使って改行+文字列までたどり着くようにしてから検索置換を実行する。

sed -i '/^$/N; :loop; /\n$/{N; b loop}; s/\n検索文字列/置換文字列/' ファイル

それぞれの意味は以下のようになる。

  • /^$/N; 空行にマッチしたら次の行を読み込む
  • :loop; ジャンプ先ラベル
  • /\n$/{N; b loop}; 改行を読み込んだらもう一行読み込んでloopにジャンプ
  •  s/\n検索文字列/置換文字列/ そうでなかったら検索置換を実行

これで直前1つの改行だけを対象に検索置換が行える。

たとえば、<body>タグの直前の改行を1つだけ削除するなら、

sed -i '/^$/N; :loop; /\n$/{N; b loop}; s/\n<body>/<body>/' *.html

 のようになる。

Perlを使う

 Perlが使えるのであれば、あまり考えなくても\nで改行文字にマッチさせることができる。

perl -pe 's/\n/,/g' ファイル

-pはsedのような出力を行うオプションである。

findと組み合わせて複数ファイルの複数行置換を行う場合は以下のようになる。

find . -name '*.html' -exec perl -pi.bak -e 's|検索文字列|置換文字列|g' {} \;

-i.bakは置換前のファイルを残すオプション。ファイル名の最後に.bakが付いたファイルが残る。-piとしておけば、ファイルを直接編集することになる。

perl -help

Usage: perl [switches] [--] [programfile] [arguments]
  -0[octal]         specify record separator (\0, if no argument)
  -a                autosplit mode with -n or -p (splits $_ into @F)
  -C[number/list]   enables the listed Unicode features
  -c                check syntax only (runs BEGIN and CHECK blocks)
  -d[:debugger]     run program under debugger
  -D[number/list]   set debugging flags (argument is a bit mask or alphabets)
  -e program        one line of program (several -e's allowed, omit programfile)
  -E program        like -e, but enables all optional features
  -f                don't do $sitelib/sitecustomize.pl at startup
  -F/pattern/       split() pattern for -a switch (//'s are optional)
  -i[extension]     edit <> files in place (makes backup if extension supplied)
  -Idirectory       specify @INC/#include directory (several -I's allowed)
  -l[octal]         enable line ending processing, specifies line terminator
  -[mM][-]module    execute "use/no module..." before executing program
  -n                assume "while (<>) { ... }" loop around program
  -p                assume loop like -n but print line also, like sed
  -P                run program through C preprocessor before compilation
  -s                enable rudimentary parsing for switches after programfile
  -S                look for programfile using PATH environment variable
  -t                enable tainting warnings
  -T                enable tainting checks
  -u                dump core after parsing program
  -U                allow unsafe operations
  -v                print version, subversion (includes VERY IMPORTANT perl info)
  -V[:variable]     print configuration summary (or a single Config.pm variable)
  -w                enable many useful warnings (RECOMMENDED)
  -W                enable all warnings
  -x[directory]     strip off text before #!perl line and perhaps cd to directory
  -X                disable all warnings

HTMLタグブロックの検索置換の基本

複数行にわたっているHTMLなどのタグブロックで、特定の文字を含むブロックを削除したいときは以下のようにする。「(?!」の表現を使うのがミソ。

perl -07 -pi -e 's|<item>((?!</item).)*?含む文字列.*?</item>||gs' ファイル

まず、ファイルを読み込む際、各行のセパレータとして\0=NULLが設定されているので、「-0」オプションで\0以外の使われていない文字を8進数で指定する。「-07」(BELL文字)や「-0777」(x1FFの無効文字)を設定すると、ファイル全部の文字列が一気に検索対象として読み込まれる。

以降はsオプション(.文字が改行にもマッチする)モードで検索置換すればOKだ。

あとは、「((?!</item).)」のような閉じタグ排他の正規表現で最小マッチを行って正確にマッチさせておけばOKだ。