sコマンド

sコマンドは、正規表現を用いて現在のパターンスペースのマッチ部分を置換する。


    sコマンドの基本

    sコマンドの書式は、s/正規表現/置換文字列/フラグ。ここで「置換文字列」とはマッチした文字列をこれで置換するということ。

    フラグは置換の動作を変更するもので、0個以上指定できる。

    アドレス部分でも正規表現を用いれば、いったん絞り込んでからのラクな正規表現を使える。また、置換文字列の中でマッチ部分を参照する&記号やマッチしたグループを番号で参照する\1(1番目のグループ)、\2…を使える。

    sedコマンドのデフォルトは基本正規表現なので、拡張正規表現(+などの表現)を有効にしたいなら--regexp-extendedオプションを付けること。


    アドレスなしの場合

    input='1行目 りんごジュース
    2行目 みかんジュース
    3行目 いちごケーキ'
    
    echo "$input" | sed 's/ジュース/アイス/'
    

    アドレスなしだと全入力行が対象になるので、出力はこうなる。

    1行目 りんごアイス
    2行目 みかんアイス
    3行目 いちごケーキ
    

    アドレス1個の場合

    input='1行目 りんごジュース
    2行目 みかんジュース
    3行目 いちごケーキ'
    
    echo "$input" | sed '/1行目/ s/ジュース/アイス/'
    

    アドレス1個なら、アドレスにマッチした入力行が対象になるので、出力はこうなる。

    1行目 りんごアイス
    2行目 みかんジュース
    3行目 いちごケーキ
    

    アドレス2個の場合

    input='1行目 りんごジュース
    2行目 みかんジュース
    3行目 いちごジュース'
    
    echo "$input" | sed '/1行目/,/2行目/ s/ジュース/アイス/'
    

    アドレス2個なら、アドレス1からアドレス2までの入力行が対象になるので、出力はこうなる。

    1行目 りんごアイス
    2行目 みかんアイス
    3行目 いちごジュース
    

    マッチ部分の参照

    例えば「数字にマッチした部分を()で囲みたいが、マッチする可能性のある数は無限にある」ようなケースなら、「実際にマッチしたもの」を利用(参照)して変換する必要がある。

    そこで、置換文字列の中で、マッチ部分を参照する&記号やマッチしたグループを番号で参照する\1(1番目のグループ)、\2…を使える。

    input='1行目 りんごジュース
    2行目 みかんジュース
    3行目 いちごケーキ'
    
    # 拡張正規表現
    echo "$input" | sed --regexp-extended 's/[1-9]+行目/(&)/'
    

    上記は、マーカーの&がマッチした文字列を表現しており、マッチ部分を()で囲む処理をしている。出力はこうなる。

    (1行目) りんごジュース
    (2行目) みかんジュース
    (3行目) いちごケーキ
    

    マッチしたグループの参照

    正規表現でグループ表現を用いた場合、マッチしたグループを置換文字列の中で番号順に\1(1番目のグループ)、\2…というように参照できる。

    input='1行目 りんごジュース
    2行目 みかんジュース
    3行目 いちごケーキ'
    
    # 拡張正規表現
    echo "$input" | sed --regexp-extended 's/([1-9]+行目) (.+ジュース)/(\1) [\2]/'
    

    上記はマッチしたグループ1を()で、グループ2を[]で囲む処理をしているので、出力はこうなる。

    (1行目) [りんごジュース]
    (2行目) [みかんジュース]
    3行目 いちごケーキ
    

    sコマンドのフラグ

    sコマンドの置換の動作を変更するフラグを0個以上指定できる。フラグが複数ならgiのように続ける。

    sコマンドのフラグ
    フラグ 意味
    g 複数マッチしたら全て置換。デフォルトでは一つ目のマッチだけ置換となる。
    番号 複数マッチのうち、この順番でマッチしたものだけ置換。
    p 置換が行われた場合、現在のパターンスペースを出力。スクリプト末尾の段階の出力は別途行われる。
    w ファイル名 置換が行われたパターンスペースだけをファイルに出力。置換が全くなかった場合、既存ファイルは空ファイルになってしまう。スクリプト末尾の段階の出力は別途行われる。
    e 置換が行われた場合、置換したパターンスペースをBashコマンドとして実行し、その出力でパターンスペースを置き換える。
    i 大文字小文字を同一視。

    gフラグ

    複数マッチしたら全て置換。

    # デフォルトでは一つ目のマッチだけ置換
    echo 'abc abc' | sed 's/abc/ABC/'
    ABC abc
    
    # gフラグでマッチ全て置換
    echo 'abc abc' | sed 's/abc/ABC/g'
    ABC ABC
    

    番号フラグ

    複数マッチのうち、この順番でマッチしたものだけ置換。

    # 2番目だけ置換
    echo 'abc abc abc' | sed 's/abc/ABC/2'
    abc ABC abc
    

    pフラグ

    置換が行われた場合、現在のパターンスペースを出力。

    input='1行目 りんごジュース
    2行目 みかんジュース
    3行目 いちごケーキ'
    
    echo "$input" | sed 's/いちご/ショート/p'
    

    上記のpフラグにより、置換が行われた場合の出力が別途あるので、出力はこうなる。

    1行目 りんごジュース
    2行目 みかんジュース
    3行目 ショートケーキ
    3行目 ショートケーキ
    

    wフラグ

    置換が行われたパターンスペースだけをファイルに出力。

    input='1行目 りんごジュース
    2行目 みかんジュース
    3行目 いちごケーキ'
    
    # ファイルnewPatternに上書きなので注意
    echo "$input" | sed 's/いちご/ショート/w newPattern'
    

    上記は置換が行われた場合、その新しいパターンスペースだけがファイルnewPatternに上書きされる。つまりnewPatternのファイル内容は、

    3行目 ショートケーキ

    ここでもし置換が全く行われなかったら、ファイルnewPatternは空ファイルになってしまうので注意。

    これとは別に標準出力は下記のように行われる。

    1行目 りんごジュース
    2行目 みかんジュース
    3行目 ショートケーキ
    

    eフラグ

    置換が行われた場合、置換したパターンスペースをBashコマンドとして実行し、その出力でパターンスペースを置き換える。

    echo 'echo "Hello world!"' | sed 's/world/sed/e'

    上記はパターンスペースの「echo "Hello world!"」という文字列を「echo "Hello sed!"」に変換し、それをBashコマンドとして実行し、その出力をパターンスペースに上書きしているので、出力はこうなる。このフラグ需要あるの?

    Hello sed!

    iフラグ

    大文字小文字を同一視。

    echo 'abc ABC AbC aBC' | sed 's/abc/xyz/gi'

    上記は元の正規表現は「abc」と小文字だが、iフラグで大文字・小文字を無視(同一視)しているので、出力はこうなる。(gフラグでマッチ全て置換)

    xyz xyz xyz xyz