sedコマンド

sedの特徴

sedはStream EDitorであり、入力ストリームから流れてくるテキストをスクリプトを使って編集するツールである。

Fizz
Buzz
FizzBuzz
FizzBuzzBuzz

このテキストを入力として、BuzzをFizzに置換するsedスクリプトを実行する。

sed '2,3s/Buzz/Fizz/' sample.txt
(out)Fizz
(out)Fizz
(out)FizzFizz
(out)FizzBuzzBuzz

'2,3s/Buzz/Fizz/'がsedの動作を指定するスクリプトである。このスクリプトは最初の2,3が「2行目から3行目」、次のsが「置換処理(Substitute)を行う」、残りの/Buzz/Fizz/が「BuzzをFizzに」という意味を持っている。

sedは以下のようにコマンドを実行する。

  1. 入力ストリームから1行読み込み、改行を取り除いたものをパターンスペースと呼ばれるバッファに記憶する。
  2. 処理条件を満たしているか検査し、満たしていた場合はコマンドを実行する。
  3. スクリプトの終端に達した場合、1.に戻り次の入力行に対して処理を行う。
    • このとき、既定でパターンスペースの内容を出力ストリームへ出力する。
    • 1.で改行を取り除いた場合、改行を追加して出力する。
    • -nオプションを付けた場合は出力しない(コマンドで明示的に出力指示があったものは除く)

このように、sedは行単位でテキストを読み込み、スクリプトに従って編集を行えるツールである。

スクリプト

sedの肝はなんと言ってもスクリプトである。スクリプトの構文は以下のようになっている。

[address]X[option]
[address]コマンドを実行する行の対象
省略した場合は全ての行
X実行するコマンド
[option]コマンドに応じたオプション

複数のスクリプト

複数のスクリプトを指定するときはEオプションを使うか、セミコロンで区切って書く。

sed -e '2,3s/Buzz/Fizz/' -e '2s/Fizz/Buzz/' sample.txt
(out)Fizz
(out)Buzz
(out)FizzFizz
(out)FizzBuzzBuzz
sed '2,3s/Buzz/Fizz/; 2s/Fizz/Buzz/' sample.txt
(out)Fizz
(out)Buzz
(out)FizzFizz
(out)FizzBuzzBuzz

1つのアドレスに対して複数のコマンドを実行するときは{}で囲み、セミコロンで区切る。

sed '2,3{s/Buzz/Fizz/; i\
(con)Hello!
(con)}' sample.txt
(out)Fizz
(out)Hello!
(out)Fizz
(out)Hello!
(out)FizzFizz
(out)FizzBuzzBuzz

ファイルからスクリプトを読み込む

fオプションを使ってスクリプトが書かれたファイルを読み込める。

sed -f sedscript.txt sample.txt

アドレス

アドレスとはsedがどの行を処理するか指し示すものである。処理条件と考えれば良い。アドレスが指定されない場合はすべての行に対して処理を行う。

行番号で指定する

処理対象を何行目かで指定するには、アドレスとして行数を書く。最終行を意味する特別な記号として$が利用できる。

144X144行目にXというコマンドを実行
$X最終行にXというコマンドを実行

start~stepを使用すると、「step行ごと」という指定ができる。数式で表せばstart~step\textrm{start} + \textrm{step}\,n \quad(\textrm{nは0以上の整数})となる。[GNU sed]

5~3X5行目から3行ごとにXというコマンドを実行
5+3n行目 (5行目、8行目、11行目、14行目、...)
0~2X偶数行でXというコマンドを実行
0+2n行目 (2行目、4行目、6行目、8行目、...)
1~2X奇数行でXというコマンドを実行
1+2n行目 (1行目、3行目、5行目、7行目、...)

正規表現で指定する

入力テキストがパターンにマッチするかで処理対象を指定できる。

/regexp/正規表現regexpに一致した行
\@regexp@正規表現regexpに一致した行
(@は他の文字でもよい)

正規表現の処理を指定するオプションI、Mを付与できる。

/regexp/I
\@regexp@I
大文字小文字を区別しないで正規表現regexpに一致した行
/regexp/M
\@regexp@M
マルチラインモードで正規表現regexpに一致した行

処理範囲を指定する

,を使うと行の範囲を指定できる。

10,3210行目から32行目まで
5,$5行目から最終行まで
/regexp1/,/regexp2/正規表現regexp1に一致した行から、regexp2に一致する行まで
20,/regexp/20行目から、正規表現regexpに一致する行まで

GNU拡張の指定

address,+Nadressに一致した行と、その後に続くN行
address,~Nadressに一致した行から、Nの整数倍の行まで
例:20,~8 の場合、20、21、22、23、24(8*3)が処理対象となる

アドレスの否定

!を付けるとアドレス指定が反転する。例えば4!は「4行目でないとき」、3,5!は「3行目から5行目でないとき」を意味する。

複数ファイルを処理するときの注意点

sedはデフォルトでは複数ファイルの入力も1つのストリームとして処理する。それによるアドレス指定の注意点を示す。

項目デフォルトの動作-iまたは-sを付けたときの動作
行番号複数のファイルにまたがって、連続して数える。ファイルごとに数える。
$の位置最後のファイルの最終行各ファイルの最終行
範囲指定の継続ファイルをまたがって範囲が検索される。
A,Bというアドレス指定がされた場合において、Aを満たす行がすでにありBを探索している状態でファイル終端に達したとき、次のファイルはBを満たす行が見つかるまで引き続き処理対象である。
ファイルごとに範囲が検索される。
A,Bというアドレス指定がされた場合において、Aを満たす行がすでにありBを探索している状態でファイル終端に達したとき、その時点で範囲は終了したと見なされる。次のファイルはAを満たす行が見つかるまで処理対象ではない。

コマンド

置換(Substitute)

s/正規表現/置換後の文字列/フラグ

置換するにはsに続けて正規表現と置換後の文字列を指定する。置換後の文字列では\n(nは1~9)で前方参照が行える。

sに続く文字

sに続く/を正規表現や置換後の文字列に含めたい場合、コマンドの/を別の文字に置き換えても良い。たとえば、s@正規表現@置換後の文字列@フラグは正しいコマンド指定である。このsに続く文字を正規表現や置換に含めるときは\でエスケープする。

GNU sedでは以下の拡張フラグを使用して、大文字小文字を変換できる。

次の文字を大文字/小文字にする大文字:\u
小文字:\l
\u\1の場合、\uの影響を受けるのは\1の1文字目だけである。もし\1がabcであれば結果はAbcとなる。
次の文字以降を大文字/小文字にする大文字:\U
小文字:\L
\Eで変換の終了位置を指定する。\U\1\Eとすると\1の文字が全て大文字になる。
また\U\1\L\2\E\3のように、反対の指定がされた場合も終了する。つまりこれは\U\1\E \L\2\E \3とみなされ\3には大文字/小文字の変換は適用されない。
拡張正規表現

sedはデフォルトでは標準正規表現(BRE)であり、Eオプションsed -Eを付けると拡張正規表現となる。

特徴BREERE
文字として扱う場合に
エスケープが必要な特殊文字
^ $ \ . [ ] *^ $ \ . [ ] * + ? { } ( ) |
グループ化\( ... \)( ... )
オルタネーションなし|
0回または1回の出現\??
1回以上の出現\++
繰り返し回数の指定\{m,n\}{m,n}
フラグ
g最初に一致したテキストではなく、一致した全てのテキストを置換する。
n ※数値n番目に一致したテキストを置換する。
p置換したとき、置換後のテキストを出力する。
w ファイル名置換したとき、置換後のテキストをファイルに出力する。
e 置換したとき、置換後のテキストをシェルコマンドとして実行する。[GNU拡張]
I
i
大文字小文字を区別しない
M
m
複数行モードの正規表現マッチを行う。

削除(Delete)

d

パターンスペースを削除する。後続のコマンドがある場合、それらはスキップされ、すぐに次の行の処理へ移る。

出力(Print)

p

パターンスペースをただちに出力する。

次の行へ移る(Next)

n

次の行へ移る。このとき、デフォルトの挙動ではパターンスペースが出力される。

挿入(Insert)

i\
Text

GNU拡張記法:

i Text

すぐにiに続く文字を出力する。この文字はエスケープ処理が行われる。

追加(Append)

a\
TEXT

GNU拡張記法:

a TEXT

行の後に追加する。このコマンドはaに続く文字をキューに入れる。現在のパターンスペースの処理が終わると、キューの内容が出力される。

aとiの実行例

iとaの処理タイミングの違いが分かる例を示す。

(out)$ sed -e "i ---" -e "/Buzz/a There's a Buzz." -e "/Fizz/i There's a Fizz." sample.txt
(out)---
(out)There's a Fizz.
(out)Fizz
(out)---
(out)Buzz
(out)There's a Buzz.
(out)---
(out)There's a Fizz.
(out)FizzBuzz
(out)There's a Buzz.
(out)---
(out)There's a Fizz.
(out)FizzBuzzBuzz
(out)There's a Buzz.

変更(Change)

c\
TEXT

GNU拡張記法:

c TEXT

行または範囲を削除し、TEXTを出力する。後続のコマンドがある場合、それらはスキップされ、すぐに次の行の処理へ移る。アドレス指定が範囲指定でない場合は行ごとにTEXTが出力されるが、範囲指定の場合は終了行に達したときに1回だけTEXTが出力される。

sed -e "1,3c Match!" -e "p" sample.txt
(out)Match!        <- 1行目から3行目はMatchに変更 pコマンドは実行されない 
(out)FizzBuzzBuzz  <- pコマンドによる4行目の出力
(out)FizzBuzzBuzz  <- デフォルトの4行目の出力
sed "c Match!" sample.txt
(out)Match!
(out)Match!
(out)Match!
(out)Match!

ホールドスペース

sedにはパターンスペースとは別にホールドスペースと呼ばれるバッファがある。ホールドスペースはその名が示す通り次の行の処理へ移っても「保持される」バッファである。ホールドスペースに関係するコマンドを示す。

hホールドスペースの内容をパターンスペースの内容で置き換える。
gパターンスペースの内容をホールドスペースの内容で置き換える。
Hホールドスペースの末尾に、パターンスペースの内容を新しい行として追記する。
Gパターンスペースの内容に、ホールドスペースの内容を新しい行として追記する。
xホールドスペースの内容とパターンスペースの内容を入れ変える。

ホールドスペースを使って、入力内容を逆順に出力する例を示す。

sed -n 'x; 1!H; ${x;p}' sample.txt
(out)FizzBuzzBuzz
(out)FizzBuzz
(out)Buzz
(out)Fizz

その他のコマンド

l nパターンスペースを「曖昧でない(unambiguous)」書式で出力する。オプションnは折り返す文字数で、指定が無いまたは0のときは折り返さない。
曖昧でない書式とは、折り返しの場所に\、行の終わりに$が付けられ、出力不可能な文字はC言語形式でエスケープ出力される。
FizzBuzzBuzzを出力すると
FizzBuzzBuzz$
FizzBuzzBuzzを7文字で折り返し出力すると
FizzBu\
zzBuzz$

となる。
r filenameファイルの内容を読み込む。読み込んだ内容はキューへ入れられ、次の行へ移るときに出力される。
ファイルが見つからない場合は何も出力されない。
ファイルの内容はsedプログラムの処理対象とはならない。
w filenameパターンスペースの内容をファイルへ書き込む。ファイルがなければ新規作成され、あれば上書きされる。
N次の行をパターンスペースへ読み込む。次の行がない場合、sedは終了する。終了するとき、デフォルトの出力処理は実行される。
Dパターンスペースに改行が含まれていない場合は、dコマンドと同様に、パターンスペースを削除し次の行の処理へ移る。改行が含まれる場合は、最初の改行までのパターンスペース内のテキストを削除した結果のパターンスペースを次の行とみなして処理を再開する。すなわち、新しい入力行を読み取らない。

オプション

-i[suffix]
--in-place[=suffix]
入力ファイルを出力結果で置き換える。
suffixがある場合、suffixを付けたバックアップファイルを作成する。
-nと併用する場合、明示的に出力したものだけが結果となる。
-n
--quiet
--silent
パターンスペースの既定出力を無効にする。
-e script
--expression=script
実行するスクリプトを指定する。複数指定可能。
-f scriptfile
--file=scriptfile
スクリプトが記録されたファイルを指定する。
-eと併用可能。
-l length
--line-llength=length
lコマンドの折り返し幅を指定する。
<推奨> -E
-r
--regexp-extended
拡張正規表現を使う。
-s
--separate
複数ファイルの入力を、1つの長いストリームではなく、分かれているものと見なす。
-u
--unbuffered
バッファの使用量を減らす。
入力からの読み込みを最小限に、出力のフラッシュを多くする。
-z
--null-data
--zero-terminated
入力を改行ではなくNUL区切りとみなす。
--posixGNU拡張を無効にする。
--debugデバッグモードで実行する。処理の詳細が出力される。
--sandboxサンドボックスモードで実行する。
e、r、wコマンドを無効にし、これらが含まれている場合は実行されることなく終了する。

参考文献

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です