置換する

置換します。

sed

sedで一気に置換します。

一気に置換すると、置換しなくていい箇所を置換してしまうこともあるので、必ずバックアップ等々が必要です。

demonstration

よくある例です。CRを削除します。


% sed -e 's/\r//' windows.txt > tmp

LFを削除します。


% sed -e 's/\n//' broken.txt > tmp

sの直後がdelimiterという決まりなので、なんでもdelimiterにできます。99%くらいは/が使われていると思いますが、部署間連携でディレクトリ構成の追加でsedでやるときがあります。/を含む置換です。/の次に使われるのは%の気がします。


sed -e 's%/job/dmac/rtl/l.v%/TOP/job/dmac/rtl/l.v%' JA.v > tmp

-eオプション

editのeかと思います。


% sed -e '編集コマンド' 置換したいファイル

% 何かのコマンド | sed -e '編集コマンド'

% sed -e '編集コマンド1' -e '編集コマンド2' 置換したいファイル

% sed -e '編集コマンド1; 編集コマンド2' 置換したいファイル

sコマンド

substituteのsかと思います。


% sed -e 's/置換前の文字列/置換後の文字列/' 置換したいファイル

delimiterはなんでもよいので。


% sed -e 's%置換前の文字列%置換後の文字列%' 置換したいファイル

% sed -e 's!置換前の文字列!置換後の文字列!' 置換したいファイル

行の最後まで置換させる。gフラグと言います。


% sed -e 's/置換前の文字列/置換後の文字列/g' 置換したいファイル

行の中に現れる置換前の文字列を2つまで置換させる。数値フラグと言います。


% sed -e 's/置換前の文字列/置換後の文字列/2' 置換したいファイル

gフラグや数値フラグが無い場合は、最初にマッチした文字列のみを置換します。

Regular Expression

sedの正規表現です。大体は網羅しています。

. 任意の1文字にマッチする。改行は除く。
^ 行の先頭にマッチする。
$ 行の最後にマッチする。
.* 任意の1文字のゼロ回以上の繰り返しを表現している。なんにでもマッチする。
[a-z]* 英字小文字のゼロ回以上の繰り返しを表現している。[a-z]*_[a-z]*などのように使う。
<\\(.*\\)> 全てのHTMLタグまたはXMLタグの名前を取り出す。1行の中のタグの名前を最大9個まで取り出すことができる。
* 直前の文字または文字列のゼロ回以上の繰り返しを表現している。
\\1 置換後の文字列で使う。ここではタグの名前を取り出す。
\\2 置換後の文字列で使う。<\\([A-Za-z]*\\)>.*<\\([A-Za-z/]*\\)>として取得しておくことで、開始タグの名前が\\1に入り、終了タグの名前が\\2に入る。
\\3 \\3から\\9まで同様。
下記のHTMLファイルからタグの名前を取り出します。

simple.html

<!DOCTYPE html>
<html>
    <head>

    </head>
    <body>

    </body>
</html>

編集コマンドはシングルクォートで囲み、引数の置換したファイルを忘れないようにしましょう。もし忘れてしまった場合はgrepと同様にCtrl + cで抜けます。


% sed -e 's/<\(.*\)>/\1/g' simple.html

結果は下記となります。


!DOCTYPE html
html
    head

    /head
    body

    /body
/html

simple.html.2

<!DOCTYPE html>
<html>
    <head>

    </head>
    <body>
        <pre><code>

        </code></pre>
    </body>
</html>

簡単なHTMLに限定して、一気に置換します。


% sed -e 's/<\([A-Za-z\/\! ]*\)>/&lt \1 &gt/g' simple.html.2

&と!はsedの中で機能を持つメタ文字の1つなので、エスケープが必要です。

結果は下記となります。(実際にはスペースは入れない方が良いでしょう。)


&lt !DOCTYPE html &gt
&lt html &gt
    &lt head &gt

    &lt /head &gt
    &lt body &gt
        &lt pre &gt&lt code &gt

        &lt /code &gt&lt /pre &gt
    &lt /body &gt
&lt /html &gt

上記を生成したい場合です。


% sed -e 's/<\([A-Za-z\/\! ]*\)>/\&lt \1 \&gt/g; s/\&/&ampamp/g' simple.html.2
&amplt !DOCTYPE html &ampgt
&amplt html &ampgt
    &amplt head &ampgt

    &amplt /head &ampgt
    &amplt body &ampgt
        &amplt pre &ampgt&amplt code &ampgt

        &amplt /code &ampgt&amplt /pre &ampgt
    &amplt /body &ampgt
&amplt /html &ampgt

さて、次はAttributeを取り出して、いや、やめておきましょう。

繰り返し回数を指定すると便利な場合があります。

[0-9]\{8\} 8桁の数字列を表現している。
[0-9]\{8,\} 8桁以上の数字列を表現している。
[0-9]\{8,10\} 8桁以上10桁以下の数字列を表現している。

例です。


% sed -e 's/<\([A-Za-z]\{4\}\)>/\< \1 \>/g' simple.html.2

yコマンド

yコマンドでは正規表現は使えません。

simple.html.3

<!DOCTYPE HTML>
<html>
    <HEAD>

    </head>
    <BODY>
        <pre><CODE>

        </code></pre>
    </body>
</html>

Hをhに、Tをtに、Mをmに、Lをlに、置換します。


% sed -e 'y/HTML/html/' simple.html.3

出力結果です。Hをhに、Tをtに、Mをmに、Lをlに、置換しているので、下記となります。

<!DOCtYPE html>
<html>
    <hEAD>

    </head>
    <BODY>
        <pre><CODE>

        </code></pre>
    </body>
</html>

まとめ

sedの機能の半分弱を紹介しました。

sedが有効な場合もありますが、プログラムなどを一気に置換して、期待しない動作になってしまった場合、デバッグの時間が非常に無駄なので、viで:%s/置換前の文字列/置換後の文字列/cgとして、1つ1つ確認しながら置換した方が全体として速い場合もあります。開発に慣れてくると一括置換を躊躇うようになると思います。

その上で、大量の一括置換の恩恵を得るために知恵を絞ることが多いと思います。

sedで一括置換して、何かがおかしくなったときに、WinMergeを使う効率化は現実的な判断だと思います。リファクタリングの後にgit diffの結果をWinMergeで確認するという手はありだと思います。git diffをわかりやくするために、異なるハッシュのバージョンを異なるフォルダーで持っておいて、WinMergeでフォルダーどうしの比較を行うと、扱いやすくわかりやすいし見やすいです。

所感

filter関連の全てに言えることですが、ある程度の複雑さを超えたら、今ならRubyのCSVライブラリ(非常に強力かつ学習コストが低い)とRubyの正規表現を利用して、Rubyに慣れた方が開発効率が良いと思います。

カテゴリーUNIX

コメントを残すために、Twitter OAuthを必要としています。ご了承ねがいます。
コメントは、Twitterに影響しません。

Twitter OAuth