sed は文字列変換を行うためのスクリプト言語の一つである。sed は、awk と同様に、コマンドや bash などと同時に使うことができ、非常に手軽である。ファイルの内容について、特定のパターンを置換したり、特定の行のパターンを置換したりすることができ、手軽でありながら強力な文字列処理機能を備えている。
sed は基本的に指定した行(アドレス)に対して指定処理を行う、という文型をとる。例えば、アドレス 1 に対して、コマンド 1 を行う場合は次のように書く。
address1command1
アドレスやコマンド(行や処理)が複数あれば、次のようにこの基本文型を繰り返せばよい。
xxxxxxxxxx
address1command1
address2command2
address3command3
アドレスの指定方法は、主に数字を使って指定する。例えば 10 行目に対して処理したければ、アドレスを 10
に記述すればよい。また、10 行目から 20 行目に対して処理したければ、アドレスを 10,20
のように記述すればよい。アドレスを指定するとき、数字の他に正規表現を使ったパターンマッチで指定することもできる。また、アドレスを省略することができる。アドレスを省略すると、すべての行に対してコマンドを実行することになる。
コマンドはアルファベット 1 文字で表される。例えば、p
ならば出力(他のプログラミング言語の print
関数に相当)、d
ならば現在の行を削除(出力しない)、i
は新しい行を挿入、s
ならば置換を意味する。以下に、具体例を用いて、アドレスとコマンドの書き方を説明する。
samples ディレクトリにある murphys_law.txt ファイルを使って sed の使い方を説明する。まず、簡単な例として、murphys_law.txt ファイルの 5 行目の内容を出力させてみる。5 行目の内容を出力したいので、アドレスが 5
で、コマンドが p
となる。したがって、sed を次のように実行すればよい。
xxxxxxxxxx
cd
cd unix4bi/samples
sed '3p' murphys_law.txt
## Smile, tomorrow will be worse.
## Every solution breeds new problems.
## Everything goes wrong all at once.
## Everything goes wrong all at once.
## Nothing is as easy as it looks.
## ...
## ...
## Things get worse under pressure.
この実行例を見ると、3 行目が 2 回出力されて、それ以外の行が 1 回だけが表示されている。少し予想外の結果となった。実は、sed では、処理後の文字列をすべて出力する仕様となっている。(置換などの)処理が書かれていない場合は、各行の内容がそのまま出力される。それが上のような結果となった。しかし、3 行目の文字列に着目してみると、それが 2 回出力されている。この 2 回目は 3p
の処理によって出力されたものである。
3p
のコマンドで 3 行目だけを出力させられなかった。そこで、sed のデフォルトの処理によって出力されるような情報を抑制すれば、3p
のコマンドの機能によって出力される情報だけが表示されるようになる。sed のデフォルトの出力を抑制するには、-n
オプションを使用する。
xxxxxxxxxx
sed -n '3p' murphys_law.txt
## Everything goes wrong all at once.
同様にして、3 行目から 6 行目までのデータを出力してみる。
xxxxxxxxxx
sed -n '3,6p' murphys_law.txt
## Everything goes wrong all at once.
## Nothing is as easy as it looks.
## Anything that can go wrong will go wrong.
## Matter will be damaged in direct proportion to its value
次に、正規表現を使用して行の抽出を行ってみる。murphys_law.txt ファイルのうち、"will" を含む行を出力してみる。
xxxxxxxxxx
sed -n '/will/p' murphys_law.txt
## Smile, tomorrow will be worse.
## Anything that can go wrong will go wrong.
## Matter will be damaged in direct proportion to its value
## If anything simply cannot go wrong, it will anyway.
## If there is a worse time for something to go wrong, it will happen then.
このように -n
オプションと p
コマンドを利用することで特定の行を抽出することができる。
sed の行を削除するコマンドが d
である。例えば、murphys_law.txt の内容のうち、3 行目から 19 行目までのデータを削除する場合は、次のようにする。そうすると、このファイルの 21 行のデータのうち、1、2、20、21 行目のデータだけが残って出力されるようになる。
xxxxxxxxxx
sed '3,19d' murphys_law.txt
## Smile, tomorrow will be worse.
## Every solution breeds new problems.
## New systems generate new problems.
## Things get worse under pressure.
次に、sed のコマンドを続けて実行してみる。murphys_law.txt の内容のうち、6 から 14 行目以外の行に対して、"new" を含む行を出力してみる。これを行うためには、まず sed を 6,14d
で実行し、その実行結果をパイプで 2 番目の sed に渡す。2 番目の sed において、行の内容が "new" を含めば、その行の内容を出力するようになる。
xxxxxxxxxx
sed '6,14d' murphys_law.txt | sed -n '/new/p'
## Every solution breeds new problems.
## New systems generate new problems.
次に、行頭に "If" を含む行を削除し、行頭に "If" を含まない行だけを出力する例を書く。この処理は、例えば CSV ファイルのメタデータ(各行の先頭に "#" がある行)を削除したりする際に便利である。
xxxxxxxxxx
sed '/^If/d' murphys_law.txt
## Smile, tomorrow will be worse.
## Every solution breeds new problems.
## ...
## ...
## Things get worse under pressure.
文字列の置換は s
コマンドで行う。まず、簡単な例として、murphys_law.txt の最初の 5 行に対して、小文字の "a" を大文字の "A" に置換してみる。置換コマンド s
の後ろにある /a/A/g
は置換コマンドのオプションである。具体的にすべて(g
)の "a" を "A" に置換せよという意味を表している。
xxxxxxxxxx
sed '1,5s/a/A/g' murphys_law.txt
## Smile, tomorrow will be worse.
## Every solution breeds new problems.
## Everything goes wrong All At once.
## Nothing is As eAsy As it looks.
## Anything thAt cAn go wrong will go wrong.
## Matter will be damaged in direct proportion to its value
## If anything simply cannot go wrong, it will anyway.
## ...
コマンドの出力結果を見ると、最初の 5 行では確かに "a" がすべて "A" に置換されている。しかし、6 行目以降では "a" が "a" のままであることがわかる。置換されていない行を出力させたくない場合は、最初の 5 行を抽出してから置換を行えばよい。
xxxxxxxxxx
sed -n '1,5p' murphys_law.txt | sed "s/a/A/g"
## Smile, tomorrow will be worse.
## Every solution breeds new problems.
## Everything goes wrong All At once.
## Nothing is As eAsy As it looks.
## Anything thAt cAn go wrong will go wrong.
次に、すべての行に対して "a" を "A" に置換してみる。すべての行に対して何らかの操作を行うときは、アドレスを省略して書く。すなわち、s
コマンドの前にある 1,5
のようなドレスを省略して書く。
xxxxxxxxxx
sed 's/a/A/g' murphys_law.txt
## Smile, tomorrow will be worse.
## Every solution breeds new problems.
## Everything goes wrong All At once.
## ...
## ...
## Everything tAkes longer thAn you think.
## New systems generAte new problems.
## Things get worse under pressure.
ファイルの末尾に ";" を付け加えてみる。ファイルの末尾は正規表現で $
と表される。置換コマンドでファイルの末尾 $
を ;
に置換すれば、見かけ上ファイルの末尾に ";" を加えていることになる。
xxxxxxxxxx
sed 's/$/;/g' murphys_law.txt
## Smile, tomorrow will be worse.;
## Every solution breeds new problems.;
## Everything goes wrong All At once.;
## ...
## ...
## Everything tAkes longer thAn you think.;
## New systems generAte new problems.;
## Things get worse under pressure.;
sed コマンドを使った文字列置換では後方参照の機能を利用できる。この機能を説明するためには、正規表現から説明しなくてはならないので、ここでは割愛する。この後方参照の機能は様々なところで役立つので、時間あるときにぜひ正規表現のところから勉強するとよい。
行の挿入を行うコマンドには i
および a
がある。i
は現在の行よりも 1 つ前に挿入するのに対して、a
は現在の行よりも 1 つ後の行に挿入する。murphys_law.txt の "Every" から始まる行があれば、その次の行に "--- --- --- ---" を挿入してみる。
xxxxxxxxxx
sed '/^Every/i --- --- --- ---' murphys_law.txt
## Smile, tomorrow will be worse.
## --- --- --- ---
## Every solution breeds new problems.
## --- --- --- ---
## Everything goes wrong all at once.
## Nothing is as easy as it looks.
## ...
## ...
## When all else fails, read the instructions.
## --- --- --- ---
## Everything takes longer than you think.
## New systems generate new problems.
## Things get worse under pressure.
なお、macOS を使用している場合は、"command i expects \ followed by text" のエラーメッセージが表示される。これは macOS ではこのような使い方をサポートしていない。macOS に gsed をインストールし、その後、sed
コマンドを使用する代わりに gsed
コマンドを使用するとエラーなく実行できる。
samples ディレクトリにある murphys_law.txt ファイルの各行の最後はピリオド .
で終わっている。行の最後にあるピリオドをセミコロン ;
に書き換えよ。
x
samples ディレクトリにある murphys_law.txt ファイルの各行に単語 "worse" を含むかどうかを検索し、"worse" を含む行の下に "xxx xxx xxx xxx" という行を新たに追加せよ。
xxxxxxxxxx
samples ディレクトリにある murphys_law.txt ファイルの最初の 10 行に含まれている単語 "go" をすべて "went" に書き換えよ。
xxxxxxxxxx