sed - テキストを置換や編集をする

sed

sedコマンドは、主にテキストを置換することに使われます。
テキスト編集などにとても便利なコマンドでぜひ覚えておきたいコマンドのひとつです。

目次

sedとは

sedとはストリームエディタ(stream editor)の略で、入力ファイルやパイプラインで渡されたテキストを編集することができます。

普通のエディタと違い、パイプライン渡されたテキストを編集できることが特徴です。
また、ファイルにある特定の文字列を一括に編集できるのも利点といえるでしょう。

sedコマンドの特徴としては、テキストの置換がありますが、そのほかにも文字列の挿入や削除なども行うことができます。その際に正規表現のグループ化やアドレスについて知っていると複雑なテキスト編集を行うことができます。

また、パターンスペースやホールドスペースというsedコマンドの2つのバッファについて知っているとsedコマンドの処理についてより詳しくなり、さらに複雑なテキスト編集を行うことができます。

他にもsedコマンドには条件分岐の処理や複数行の処理も行うことができます。
条件分岐の処理を使いこなすことができれば、sedコマンドの置換コマンドであるsコマンドをよりうまく使うことができます。
複数行の処理を行うことができれば、連続している複数行を一つの行にまとめる事等の処理を行うことができます。

sedコマンドの構文

sedコマンドの構文

オプション:
sedコマンドに対するオプションです。

スクリプト:

で表現されます。また、スクリプトは以下のように記述することができます。

addr:行の位置や範囲を示すもので、行番号や正規表現を用いて指定できます。
X:sedコマンドの機能を決定する、一文字のことです。
options:Xで指定したものによって必要なオプションのことです。

入力ファイル:
1個以上の入力ファイルを指定できます。

sedコマンドに入力ファイルがないもしくは-(ハイフン)の場合は、標準出力に対して、sedコマンドのスクリプトを実行します。

上から順に
1つ目は入力がファイルがない場合です。
2つ目は-(ハイフン)を用いた場合です。
3つ目はリダイレクトによって、ファイルを標準入力としてsedコマンドに渡した場合です。

それぞれsedコマンドで、標準出力に対して処理を行うという意味で同じになります。

sedコマンドのファイルの保存方法について

sedコマンドの出力は、オプションがない場合、標準出力に現れます。
また、sedコマンドの-iオプションをつけた場合、対象のファイルに対して出力を行います。

ファイルに保存する方法として、一つは、リダイレクトを利用して別のファイルを作成し、その後にmvコマンドなどでtmp.txtをitemtext.txtに置き換える方法があります。

また、-iオプションを用いて、直接ファイルを置き換えてしまう方法があります。

-iオプションは、GNU sedで利用できます。

-iオプションは、実際には一時ファイルが作られ、その一時ファイルに対してsedコマンドの出力が行われ、その一時ファイルの名前を対象のファイルとして置き換えます。

-iオプションで置き換える前のバックアップファイルが欲しい場合は、

のように-iオプションの後に接尾辞を指定することでバックアップファイルを作成することができます。

この例ではitemtext.txt.bakのような置き換える前のバックアップファイルを作成することができます。

パターンマッチした文字列を別の文字列に変換
(s/regexp/replacement/flags)

s/regexp/replacement/flagsは、sedコマンドの主要な機能といえるものです。

正規表現(Regular Expression)regexpにマッチしたものを別の文字列replacementに置き換えます。
また、flagsは0個以上を指定することでき、flagsによって文字列の置き換え方などを変更することができます。

最初にパターンマッチした文字列を置換
(flags:なし)

s/regexp/replacement/は、それぞれの行で最初に登場した文字列を別の文字列に置き換えます。
コマンド例では、最初に登場したappleという文字列をmelonという文字列に置き換えます。

コマンド例

itemtext.txt

実行結果

それぞれの行に現れるappleをすべて置き換えるわけではないので注意が必要です。

すべてのパターンマッチした文字列を置換
(flags:g)

s/regexp/replacement/gはそれぞれの行に登場するすべての正規表現にマッチした文字列を別の文字列に置き換えることができます。
コマンド例では、行に登場したすべてのappleをmelonに置き換えます。

コマンド例

itemtext.txt ▼表示

実行結果

n番目のパターンマッチした文字列を置換
(flags:数字)

s/regexp/replacement/1等のflagsに数字を入れたものは、n番目に登場したものだけを別の文字列に変更することができます。
コマンド例では、2番目に登場したスペースを:に変更しています。

コマンド例

itemtext.txt ▼表示

実行結果

1番目のスペースを,に2番目に登場したスペースを:に変更する場合は以下のようにします。

コマンド例

実行結果

-eオプションは、追加のスクリプトを記述することで変更することができます。
-eオプションは記述された順番に処理されます。

この例では、最初の-eオプションのスクリプトで1番目のスペースが,に変更されます。
その次に変更された状態のテキストで、次の-eオプションのスクリプトで1番目のスペース(元ファイルの2番目のスペース)を:に変更しています。

また、追加のスクリプトを記述する方法として;を用いて記述する方法もあります。以下のコマンド例でも上と同様の実行結果を得ることができます。

コマンド例

別の文字列に置換された行を表示
(オプション:-n, flags:p)

pフラグは変更があった行のみを表示します。
-nオプションは、スクリプトで明示的に表示する行を除き、sedコマンドで出力する行を表示しないようにします。

コマンド例

itemtext.txt ▼表示

実行結果

ここで、-nオプションをつけない場合は、変更があった個所だけが重複して表示されてしまうことに注意が必要です。

コマンド例

実行結果

別の文字列に置換された行をファイルに出力
(flags:w filename)

wフラグはファイル名を指定することでそのファイルに変更があった行を書き込みます。
また、ファイルがない場合は作成され、ある場合は上書きされます。

変更があった行を別のファイルに保存したい場合に用いることができます。

コマンド例

itemtext.txt ▼表示

実行結果(標準出力)

実行結果(change.txt)

グループ化を用いて置換文字列にパターンマッチした文字列を使用

グループ化とは、正規表現の特徴の一つでグループ化したパターンをキャプチャして、置換文字列で使うことができます。

コマンド例では、それぞれの行に対して、最初の単語を最後の単語として追記します。

例えば、「apple onion yuzu」なら、最初の単語の「apple」をその行の最後に追記して「apple onion yuzu apple」にします。

コマンド例

itemtext.txt ▼表示

実行結果

sedコマンドでの正規表現のグループ化は「\(」と「\)」で囲むことで行うことができ、その後に「\1」、「\2」のように呼び出すことができます。

呼び出す数字に関して、「\(」が現れる順番
で1から保存されます。
この例では、最初の外側にある「\(」と「\)」は行の全体を表し「\1」で呼び出すことができます。
次の内側にある「\(」と「\)」は最初の単語を表し「\2」で呼び出すことができます。
つまり、この例のパターンは行全体を表して、行全体が置き換える対象になっています。
そして、sedコマンドで行全体を書き換えるようなイメージになっています。
「[^ ]」はスペース以外の文字を表し、「*」は直前の文字の0回以上繰り返しを示す記号になります。

パターンスペースとホールドスペース

sedコマンドは、基本的に入力行をバッファに入れます。
このバッファのことをパターンスペース(pattern space)といいます。

また、sedコマンドは、パターンスペースとは別に補助的なバッファを持ちます。
これをホールドスペース(hold space)といいます。

sedコマンドの基本動作は行ごとに、

1.入力の行をパターンスペースに入れる。
2.sedコマンドのスクリプトを実行する。(パターンスペースの変更や表示等)
3.パターンスペースを表示する。(-nオプションがある場合は行わない)

を繰り返します。

sedコマンドのスクリプトのアドレスは一種の条件分岐のように扱うことができます。
つまり、アドレスを用いることでパターンスペースとホールドスペースをうまく使うことができます。

-nオプションについて

sedコマンドは、デフォルトではスクリプトの最後にパターンスペースを自動的に表示します。
-nオプションは、この自動表示をオフにします。
パターンスペースは、スクリプトでpコマンド等を明示的に記述しない限り、表示されなくなります。

目的によっては、デフォルトの自動表示が不必要になる場合があります。
そんな時に、sedコマンドの-nオプションを覚えておくと便利です。

スクリプトをファイルから読み込む
(-fオプション)

-fオプションは、sedコマンドのスクリプトが書かれたファイルを読み込むことができます。
この例では、script.sedがsedコマンドのスクリプトが書かれたファイルになります。

script.sedでは、sコマンドを用いてそれぞれの行で初めに現れた「apple」を「melon」に変えるスクリプトが書かれています。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

sedコマンドのスクリプトのコメントについて、#の記号が利用できます。

script.sed

行の後の行にテキストを追記
(aコマンド)

aコマンドは行の後の行にテキストを追記することができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

複数行のテキストを追記する場合は以下のようにします。

コマンド例

script.sed

実行結果(一部省略)

行の前の行にテキストを追記
(iコマンド)

iコマンドは行の前の行にテキストを追記することができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

また、複数行のテキストを追記する場合は以下のようにします。

コマンド例

script.sed

実行結果(一部省略)

アドレスについて

スクリプトは

で表現されます。
addr:行の位置や範囲を示すもので、行番号や正規表現を用いて指定できます。
X:sedコマンドの機能を決定する、一文字のことです。
options:Xで指定したものによって必要なオプションのことです。

ここでは、addr、つまりアドレスの指定方法についていくつか紹介します。

アドレスを指定しない

アドレスを指定しない場合、すべての行に対してコマンドを実行します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

数字で指定
(num)

コマンドの前に数字を記述するとその行番号の行に対して、コマンドを実行します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

範囲で指定
(num,num)

アドレスを範囲で指定する場合は2つの数字をカンマで区切ります。
コマンド例では1から5行目に対して、コマンドを実行することができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

最終行を指定
($)

最終行を指定する場合、$記号で指定することができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

最初の行を指定する場合は、数字で1を指定することで最初の行を指定することができます。

正規表現で指定
(/regexp/または\%regexp%等)

正規表現でアドレスを指定します。
例えば、コマンド例ではappleという単語が含まれている行に対して、コマンドを実行します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

正規表現で指定する場合、「/」の記号で囲むほかに「\」と「%」等のような一文字の記号を組み合わせても正規表現を行うことができます。

別の記号を用いて正規表現を用いると場合によって、エスケープシーケンスを使うことを減らすことができます。

別の記号を用いた場合のscript.sedの例1

別の記号を用いた場合のscript.sedの例2

別の記号を用いた場合のscript.sedの例3(非推奨)

最後の例は一応アルファベットなどの記号も利用できるという例で、どこまでが区切りであるかがわかりにくいため、使用することを推奨しません。

正規表現で範囲を指定
(num,/regexp/)

アドレスの範囲は数字と正規表現を組み合わせることでも作ることができます。
また、数字と正規表現で範囲を作る際、0という数字を使うことができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

0,/regexp/1,/regexp/は微妙に意味が違います。

0,/regexp/の場合

コマンド例

script.sed

実行結果

1,/regexp/の場合

コマンド例

script.sed

実行結果

これは、0,/regexp/の場合、「apple apple apple」という行が範囲の終了を表す/regexp/にマッチするため、itemtext.txtの最初の行の「apple apple apple」にマッチした時点で範囲が終了します。

しかし、1,/regexp/の場合はitemtext.txtの最初の行の「apple apple apple」が範囲の開始を表す1の方にマッチするため、範囲の終了を表すitemtext.txtの2つ目の「apple apple apple」という行が登場するまで、コマンドの実行が行われます。

このような点で0,/regexp/1,/regexp/は異なります。

アドレスの範囲の終端を相対的に指定
(addr1,+num)

範囲の始点から次のnum行までを指定することができます。
コマンド例では、5行目から次の3行目、つまり8行目までを指定しています。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

アドレスをステップで指定
(first~step)

first行からstep行ずつを指定することができます。
コマンド例では、5行目から3行ずつ行を指定しています。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

範囲の終端を行数の倍数で指定
(addr1,~num)

範囲の始点からnum行の倍数までの行を指定します。
コマンド例では、5行目から4行目の倍数、つまりこの場合は8行目まで指定されます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

アドレスで指定しなかったものを指定

アドレスの最後に「!」をつけることで、アドレスで指定したものとは逆、つまり、アドレスで指定しなかったものを指定することができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

アドレスで指定された行を別の文字列に置換
(cコマンド)

cコマンドは行を別の文字列に置換します。
アドレスが指定されていない場合はそれぞれの行について、文字列を置換します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

範囲が指定されている場合は、それらの行を削除してから、文字列を置換します。

コマンド例

script.sed

実行結果

!を用いて、アドレスの範囲の逆を指定すると、アドレス指定がある範囲でcコマンドでアドレスを指定しなかった時と同じような動作になります。

コマンド例

script.sed

実行結果

cコマンドは実行された後にスクリプトがすぐに終了するため、その後にあるsedコマンドのスクリプトは実行されないことに注意しましょう。

現在の入力行の行番号を表示
(=コマンド)

=コマンドは、入力行の行番号を表示することができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

sedコマンドの-nオプションで余分な出力を除くと行番号のみを表示することができます。

コマンド例

script.sed

実行結果

アドレスで正規表現を使うと、その正規表現とマッチする行の行番号を表示することもできます。

例えば、行の終わりにappleの単語がある行の行番号を表示するとしたら、

コマンド例

script.sed

実行結果

のように表示させることができます。正規表現の$は行の終わりを示す記号になります。

パターンスペースを明確な形式で表示
(lコマンド)

lコマンドは、パターンスペースを明確な形式(unambiguous form)で表します。

ここで、明確な形式とは、

表示できない文字をC言語のエスケープ形式で示す。
長い行は\の文字で区切る。
それぞれの行の終端は$の記号で終了する。

というものになります。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

UTF-8の場合で、日本語は8進数形式で表示されます。

例えば、「あ」の場合だと「\343\201\202」のように表示されます。

コマンド例

実行結果

lコマンドの後に数字を入力するとその数字分の文字で行を折り返します。

コマンド例

script.sed

実行結果

パターンスペースの内容をファイルに出力
(wコマンド)

wコマンドは、パターンスペースの内容をファイルに書き出すことができます。
コマンド例では、itemtext.txtの3行目の内容を標準出力に出力します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

/dev/stdoutを指定すると標準出力を指定することができます。
ほかにも、/dev/stderrを指定すると標準エラー出力を指定することができます。

ファイルから読み込んで内容を挿入
(rコマンド)

rコマンドは、指定したファイルの内容を次の行に挿入します。
コマンド例では、itemtext.txtの3行目の次の行にファイルの内容を挿入しています。

コマンド例

script.sed

text.txt

itemtext.txt ▼表示

実行結果

/dev/stdinを指定すると標準入力を指定することができます。

補足として、rコマンドはファイルの内容を次の行に対して挿入するが、工夫を行えば、手前に対して挿入しているかのように動作させることができます。

コマンド例

script.sed

実行結果

まず、初めのsedコマンドで最後の行にテキストを追記します。

このテキストの追加は、次のsedコマンドでパターンスペースとホールドスペースを入れ替えるxコマンドを使って、行をずらして出力させるためです。
行をずらして表示しますと最後の行が出力されません。
そのために最後の行に対して追加で何かテキストを追加します。

2つ目のsedコマンドを用いるとほとんど実行結果のように確認できます。
しかし、一番上の行に空行ができます。
これは最初のパターンスペースとホールドスペースの入れ替えにより、ホールドスペースの初期値が出力されるためです。
また、ホールドスペースの初期値は何もないため、一番上の行に空行が表示されます。

また、script.sedについて、「$q」を用いて、rコマンドで最後の行でファイルの内容を挿入させる前にsedコマンドのスクリプトを終了させます。
また、qコマンドはsedコマンドのスクリプトは終了させるコマンドです。

3つ目のsedコマンドで一番上にできた空行を削除します。そうすると上の実行結果のように出力されます。

また、script.sedの「$q」を削除して、3つ目のsedコマンドで出力の最後の行を削除しても、同様の実行結果ができます。

コマンド例

スクリプトの複数コマンドを実行(-eオプション等)

スクリプトの複数コマンドを記述するには、様々な方法があります。
itemtext.txtの最初の行と最後の行を表示する例を出して、説明します。

itemtext.txt ▼表示

複数の-eオプションで記述

複数の-eオプションを使用することで、スクリプトの複数コマンドを記述することができます。
また、スクリプトのコマンドは、-eオプションが記述された順番で実行されます。

コマンド例

実行結果

コマンドを ; で区切って記述

スクリプトのコマンドを;で区切って記述することで複数のコマンドを実行させることができます。

コマンド例

実行結果

ファイルに記述

スクリプトをファイルに記述することで、複数のコマンドを実行させることができます。

コマンド例

script.sed

実行結果

記述方法としては、;で複数のコマンドを記述したほうがタイピング量も見栄えも良いと思います。
しかし、使い方によっては、ほかの方法も使用することがあると思います。

また、-eオプションと-fオプションを同時に使用した場合の結果は以下の通りになります。

コマンド例

script.sed

実行結果

-fオプションと-eオプションの順番を入れ替えた場合は以下の通りです。

コマンド例

実行結果

この結果から、初めに呼ばれた-eオプションまたは-fオプションでのスクリプトを読み込み、その後に次の-eオプションまたは-fオプションでのスクリプトを読み込み、実行させることがわかります。

文字の置換(yコマンド)

yコマンドは、文字を別の文字に置換することができます。
yコマンドは、y/変換前の文字/変換後の文字/で記述します。
コマンド例

script.sed

itemtext.txt ▼表示

実行結果

パターンスペースを表示(pコマンド)

pコマンドは、パターンスペースを表示することができます。
また、よく使うコマンドのひとつです。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

パターンスペースを削除(dコマンド)

dコマンドは、パターンスペースを削除することできます。
dコマンドを使った場合、その後にスクリプトが続いていても、次のサイクルに移行します。
また、dコマンドもよく使うコマンドのひとつです。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

スクリプトを終了(qコマンド)

qコマンドは、スクリプトを終了させることができます。
その後のスクリプトは実行されません。
また、qコマンドの後にexitコードをつけることができます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

exitコードをつけると、終了ステータスがそのexitコードになります。
コマンド例

終了ステータスの確認

パターンスペースを次の行に飛ばす(nコマンド)

nコマンドは、sedコマンドに-nオプションがない場合は、現在のパターンスペースを表示して次のスクリプトのコマンドを実行します。
-nオプションがある場合、パターンスペースが次の行になります。

-nオプションがない例

3の倍数の行をdeleteという単語に書き換える例です。

コマンド例

itemtext.txt ▼表示

実行結果

-nオプションがある場合の例

3の倍数の行を表示する例です。

コマンド例

itemtext.txt ▼表示

実行結果

コマンドをグループ化する

コマンドのグループ化は、アドレスを指定して複数のコマンドを使用する場合に便利です。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

ホールドスペースの利用

sedコマンドでは、現在編集中の行をそれ以降の行でも使用したい場合にホールドスペースが利用できます。
しかし、ホールドスペースというバッファは一つしか利用できないので注意が必要です。

パターンスペースをホールドスペースへ
(hコマンド)

hコマンドは、パターンスペースの内容をホールドスペースにコピーできます。
コピーする際、ホールドスペースが保存していた内容は削除されます。

ホールドスペースに何かを保存した場合、gコマンド等でパターンスペースに呼び戻さないとホールドスペースの内容を使うことができません。
コマンド例は次のgコマンドを参照してください。

ホールドスペースをパターンスペースへ
(gコマンド)

gコマンドは、ホールドスペースの内容をパターンスペースにコピーできます。
コピーする際、パターンスペースが保存していた内容は削除されます。

コマンド例は、最初の1行目の内容をホールドスペースに保存します。
そして、それ以降の行の「onion onion onion」にマッチングした行で、そのマッチングした行の内容を表示した後に、ホールドスペースの内容を表示します。

コマンド例

itemtext.txt ▼表示

script.sed

実行結果

script.sedの3行目のコマンドのグループ化を利用しました。
なぜなら、コマンドを別々にした場合、うまく動作しませんでした。
コマンド例

script.sed

実行結果

アドレスでの正規表現はパターンスペースの内容を参照して、マッチングを行っているかもしれません。
なので、アドレスでの正規表現を用いる場合は、コマンドのグループ化を用いて呼び出したほうが良いと思います。

パターンスペースをホールドスペースへ追記
(Hコマンド)

Hコマンドは、ホールドスペースの内容をパターンスペースに追記することができます。
追記する際に、現在のホールドスペースの内容に改行を入れます。

コマンド例は、1行目の内容をホールドスペースに保存し、2行目の内容をホールドスペースに追記します。
その後に、それ以降の行の「onion onion onion」にマッチングした行で、そのマッチングした行の内容を表示した後に、ホールドスペースの内容を表示します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

ホールドスペースをパターンスペースへ追記
(Gコマンド)

Gコマンドは、ホールドスペースの内容をパターンスペースに追記することができます。
追記する際に、現在のパターンスペースの内容に改行を入れます。

コマンド例は、1行目の内容をホールドスペースに保存します。
その後に、それ以降の行の「onion onion onion」にマッチングした行で、ホールドスペースの内容をパターンスペースに追記して、パターンスペースの内容を表示します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

パターンスペースとホールドスペースを交換
(xコマンド)

xコマンドは、パターンスペースの内容とホールドスペースの内容を入れ替えることができます。

コマンド例では、1行目の内容「apple apple apple」をホールドスペースに保存します。
その後に、それ以降の行の「onion onion onion」にマッチングした行で、パターンスペースの内容「apple apple apple」とホールドスペースの内容「onion onion onion」を入れ替え、入れ替えたパターンスペースの内容「onion onion onion」表示します。
さらに、パターンスペースの内容「onion onion onion」とホールドスペースの内容「apple apple apple」を入れ替えて、入れ替えたパターンスペースの内容「apple apple apple」を表示します。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果

条件分岐処理

ラベル

sedコマンドのスクリプトは、プログラミングのgoto文のようにスクリプトのコマンドのどこかにラベルをつけることができます。
ラベルは:markや:labelのように、:と文字列でつけることができます。

このラベルは、bコマンドやtコマンド等で使用することができます。
ラベルの付け方は以下のようになります。

script.sed

無条件分岐
(bコマンド)

bコマンドは、無条件分岐のコマンドで、bコマンドで指定したラベルにジャンプします。
また、bコマンドにラベルが何も指定していない場合、次のサイクルに移行します。

コマンド例では、初めにappleのある行が削除され、それ以外の行が表示されます。

コマンド例

script.sed

itemtext.txt ▼表示

実行結果