まとめて処理する - { }

前回まで、sedコマンドでは処理する行や範囲をアドレスや正規表現によって指定できることを説明してきた。処理1つ1つは単純なものなので、似たような指定を何度も記述しなければならないこともある。

そんな場合、次のように-eオプションを使うと、1行のコマンドに複数の処理指定を含めることができる。


sed -e 指定 -e 指定 -e 指定 -e 指定 ... ファイル

また、似た感じではあるが、sedコマンド自体の機能ではなく、シェルのパイプラインを使って次のように処理させることもできる。


sed 指定 ファイル |
sed 指定 |
sed 指定 |
...
sed 指定

ケースバイケースだが、もし使っているマシンがマルチコア・スレッドのマシンならば、パイプラインでつないで複数同時に処理するほうが処理速度が高速になることがある。本当にうまくいくと、パイプラインを使っただけで処理が高速になることもあるので、実際に処理速度を計測して試してみてほしい。

sedコマンドは、このほかにも複数の処理を指定する方法を用意している。具体的には、「{ }」で囲むと、その範囲内の指定を順次実行するようになるのだ。記述は次のようになる。


sed '{
    指定
    指定
    ...
    指定
}
'

処理範囲をアドレス指定で区切ることもできるので、その場合には次のように記述する。


sed '
アドレス {
    指定
    ...
    指定
}
アドレス {
    指定
    ...
    指定
}
...
アドレス {
    指定
    ...
    指定
}
'

また、「{ }」による処理のグルーピングは入れ子構造にすることもできる。


sed '
アドレス {
    指定
    アドレス {
        指定
        アドレス {
            ...
        }
        ...
    }
}
'

このグルーピング機能を使うと、sedコマンドをスクリプト言語のように使うことも可能になる。読みやすいかどうかは別として、テキストをざーっと流しながら加工するような処理をする場合、sedコマンドだけである程度処理できるのだ。sedコマンドでは、置換と範囲指定した取り出し・削除あたりが使えればよいので必須ではないが、覚えておいて損はないだろう。

スクリプト言語っぽく使ってみよう

「{ }」を使って処理をまとめる場合の例を見てみよう。まず、前回も使った次のテキストデータを用意するものとする。


NAME
     sed - stream editor

SYNOPSIS
     sed [-Ealnru] command [file ...]
     sed [-Ealnr] [-e command] [-f command_file] [-I extension]
         [-i extension] [file ...]

DESCRIPTION
     The sed utility reads the specified files, or the standard
     input if no files are specified, modifying the input as
     specified by a list of commands.  The input is then written
     to the standard output.
これを次のようなsedコマンドを使ってシェルスクリプトで処理してみよう。

#!/bin/sh

A=ABCDEFGHIJKLMNOPQRSTUVWXYZ
a=abcdefghijklmnopqrstuvwxyz
sed '{
    1i\
TRANSFORMED DOCUMENT\
====================\

    /^NAME$/,/^$/ {
        /^NAME$/ {
            y/'$A'/'$a'/
            p
            s/./_/g
            p
            i\

            d
        }
        p
        d
        i\

    }
    /^SYNOPSIS$/,/^$/ {
        /^SYNOPSIS$/ {
            y/'$A'/'$a'/
            p
            s/./_/g
            p
            i\

            d
        }
        p
        d
        i\

    }
    /^DESCRIPTION$/,/^$/ {
        /^DESCRIPTION$/ {
            y/'$A'/'$a'/
            p
            s/./_/g
            p
            i\

            d
        }
        /^    / {
            s/^    //
        }
        p
        d
        i\

    }

}' $1

実行すると、次のようになる。


#!/bin/sh

sed '{
1i\
TRANSFORMED DOCUMENT\
====================\

/^[A-Z][A-Z]*$/ {
    y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
    p
    s/./_/g
    p
    i\

    d
}
/^  */ {
    s/^  *//
}
}' $1

なんちゃってオンラインマニュアル→マークダウン変換コマンドといったところだ。

正規表現を使った範囲指定と「{ }」による処理のまとめ、入れ子構造で処理対象を絞り込み、そこに置換などの処理を入れている。これまでに説明していない機能は、「y」による置換と「i」による任意の文字列の出力といったところだろう。このスクリプトだけみると、これは本当に呪文のように見えるかもしれない。

sedコマンドをスクリプト言語のように使った場合、正直あまり理解しやすいとは言いづらい。テキストデータに対し、上から下に向かって一定の変換処理をするだけなら、sedコマンドのこの機能はとても便利なのだが、書いたスクリプトを後から自分が読んでわかるかどうかは別問題だ。

ここまでマスターする必要はないと思うが、sedコマンドではこうしたこともできるんだなということは知っておいて損はない。もしかするといつかどこかで、この機能が必要なシーンに遭遇するかもしれない。もしそんなときが来れば、この機能はとても便利で強力だ。