初心者向けsed eオプション入門:これで複数編集も怖くない

はい、承知いたしました。「初心者向けsed eオプション入門:これで複数編集も怖くない」の詳細な説明を含む記事を約5000語で執筆します。


初心者向けsed eオプション入門:これで複数編集も怖くない

はじめに:なぜテキスト編集の自動化が必要なのか?

あなたが日々パソコンで作業する中で、特定のテキストファイルの内容を編集する必要に迫られることはありませんか?例えば、設定ファイルの中の特定の項目をまとめて変更したり、ログファイルから不要な行を削除したり、プログラムのソースコード中の変数名を一括で修正したり… 手作業でこれらの編集を行うのは、量が少なければ問題ありませんが、ファイルが大きかったり、同じような変更を多くのファイルに適用する必要がある場合、非常に手間がかかり、ミスの元にもなります。

そんな時に絶大な威力を発揮するのが、コマンドラインで動作するテキスト編集ツールです。その中でも特に古くから広く使われているのが sed (Stream EDitor) です。sed は、入力されたテキストに対して、指定された編集コマンドを順番に適用し、結果を出力する「ストリームエディタ」です。ファイル全体をメモリに読み込むのではなく、一行ずつ処理を進めるため、非常に大きなファイルでも効率的に扱うことができます。

sed の基本的な使い方はシンプルです。「指定したパターンに一致する行を別のテキストに置換する (sコマンド)」「特定の行を削除する (dコマンド)」「指定した位置にテキストを追加する (aコマンド, iコマンド)」などです。

しかし、もし「ファイル内の ‘apple’ を ‘orange’ に変えたい 同時に ‘banana’ を ‘grape’ に変えたい」のように、一つのファイルに対して複数の種類の編集を一度に行いたい場合はどうすれば良いでしょうか?

初心者の方がここで戸惑いがちなのが、複数の編集コマンドを sed にどうやって指示すれば良いか、という点です。一つのコマンドで複数の置換や削除を行う方法を知らないと、「まず sed で ‘apple’ を ‘orange’ に変えて、その結果を一時ファイルに保存。次に、その一時ファイルに対して別の sed コマンドで ‘banana’ を ‘grape’ に変える」といった煩雑な手順を踏むことになりかねません。これは効率が悪く、スクリプトも複雑になってしまいます。

そこでこの記事で詳しく解説するのが、sed-e オプション です。-e オプションを使うことで、複数の編集コマンドを一つの sed コマンドラインに記述し、入力テキストに対してこれら複数の編集処理をまとめて一度に適用することができます。

「複数の編集なんて難しそう…」と感じる必要はありません。-e オプションの使い方は非常に直感的で、一度覚えてしまえば、これまで面倒だった複数のテキスト編集作業が驚くほど効率化されます。この記事を通じて、-e オプションの基本から実践的な使い方までを丁寧に解説していきます。さあ、sed -e の世界へ飛び込んで、テキスト編集の達人への第一歩を踏み出しましょう!

sed の基本をおさらいする

-e オプションに入る前に、sed の最も基本的な考え方とコマンドについて軽くおさらいしておきましょう。

sed は、入力テキストの各行に対して、指定されたコマンドを順番に実行します。この処理の流れを理解することが、-e オプションを使った複数のコマンド実行を理解する上で非常に重要です。

基本的な sed の構文は以下のようになります。

bash
sed 'コマンド' 入力ファイル

または、標準入力を受け取る場合:

bash
cat 入力ファイル | sed 'コマンド'

sed は、入力ファイルから一行読み込み、それを「パターン空間 (Pattern Space)」と呼ばれる一時的な領域に格納します。そして、そのパターン空間にある行に対して、指定された「コマンド」を実行します。コマンドの処理が終わった後、特別な指定がなければ、パターン空間の内容が標準出力に出力されます。このプロセスが入力ファイルの最終行まで繰り返されます。

最もよく使われるコマンドは、文字列の置換を行う s コマンドです。

s コマンド:文字列の置換

s コマンドの基本構文は以下の通りです。

s/正規表現パターン/置換文字列/フラグ

  • 正規表現パターン: 検索したい文字列やパターンを正規表現で指定します。
  • 置換文字列: パターンに一致した部分を置き換えたい文字列を指定します。
  • フラグ: 置換の動作を制御するオプションです。代表的なものに g (global, 行全体にわたって一致した全てを置換) があります。フラグがない場合、行頭から見て最初に一致したものだけが置換されます。

例:ファイル input.txt の中の最初の “old” を “new” に置換して表示する

“`bash
echo “This is an old line with old text.” > input.txt
sed ‘s/old/new/’ input.txt

出力: This is an new line with old text.

“`

例:ファイル input.txt の中の全ての “old” を “new” に置換して表示する (g フラグを使用)

“`bash
sed ‘s/old/new/g’ input.txt

出力: This is an new line with new text.

“`

その他の基本的なコマンド

  • d コマンド:一致した行を削除する
    例:ファイル input.txt の中で “old” を含む行を削除する

    “`bash
    echo “Line 1: old content” > input.txt
    echo “Line 2: some other content” >> input.txt
    echo “Line 3: another old thing” >> input.txt
    sed ‘/old/d’ input.txt

    出力:

    Line 2: some other content

    “`

  • a コマンド:一致した行の次にテキストを追加する
    例:ファイル input.txt の中で “old” を含む行の次に新しい行を追加する

    “`bash
    echo “Line 1: old content” > input.txt
    echo “Line 2: some other content” >> input.txt
    sed ‘/old/a\
    — Added Line —‘ input.txt

    出力:

    Line 1: old content

    — Added Line —

    Line 2: some other content

    ``aコマンドの後の` は改行を示し、その次の行から追加したいテキストを記述します。テキストが複数行にわたる場合も同様に \ で繋ぎます。

  • i コマンド:一致した行の前にテキストを挿入する
    例:ファイル input.txt の中で “old” を含む行の前に新しい行を挿入する

    “`bash
    echo “Line 1: old content” > input.txt
    echo “Line 2: some other content” >> input.txt
    sed ‘/old/i\
    — Inserted Line —‘ input.txt

    出力:

    — Inserted Line —

    Line 1: old content

    Line 2: some other content

    “`

アドレス指定:特定の行にコマンドを適用する

これらのコマンドは、デフォルトでは入力の全ての行に対して適用されます。しかし、特定の行番号やパターンに一致する行のみにコマンドを適用したい場合があります。これを「アドレス指定」と呼びます。

構文は [アドレス]コマンド のようになります。

  • 行番号指定:
    • 1d: 1行目を削除
    • 5s/a/b/: 5行目の ‘a’ を ‘b’ に置換
    • 1,3d: 1行目から3行目までを削除
    • 5,$s/x/y/g: 5行目から最終行まで (‘$’ は最終行を意味する) の全ての ‘x’ を ‘y’ に置換
  • パターン指定:
    • /pattern/d: pattern に一致する行を削除
    • /start/,/end/s/a/b/: start に一致する行から end に一致する行までの範囲で ‘a’ を ‘b’ に置換

例:input.txt の3行目だけ ‘third’ を ‘sanbanme’ に置換

“`bash
echo “First line” > input.txt
echo “Second line” >> input.txt
echo “Third line, this is the third line.” >> input.txt
echo “Fourth line” >> input.txt
sed ‘3s/third/sanbanme/g’ input.txt

出力:

First line

Second line

Third line, this is the sanbanme line.

Fourth line

“`

例:input.txt の ‘Second’ を含む行から ‘Fourth’ を含む行までを削除

“`bash
echo “First line” > input.txt
echo “Second line” >> input.txt
echo “Third line” >> input.txt
echo “Fourth line” >> input.txt
echo “Fifth line” >> input.txt
sed ‘/Second/,/Fourth/d’ input.txt

出力:

First line

Fifth line

“`

ここまでが sed の基本的な考え方とコマンドです。入力の各行がパターン空間を通過し、指定されたコマンドが適用されるという流れを頭に入れておきましょう。そして、一つのコマンドはアドレス指定で特定の行にのみ適用できる、という点も重要です。

複数の編集をまとめて実行したいときの課題

さて、ここからが本題です。一つのファイルに対して複数の編集を適用したい場合、どのような方法が考えられるでしょうか?

方法1:sed コマンドをパイプで繋ぐ (非効率な場合がある)

一つの簡単な方法は、複数の sed コマンドをパイプ (|) で繋げることです。

bash
cat input.txt | sed 's/apple/orange/g' | sed 's/banana/grape/g' > output.txt

この方法では、input.txt の内容が最初の sed コマンドに渡され、’apple’ が ‘orange’ に置換された結果が標準出力に出力されます。その出力が次の sed コマンドの標準入力となり、今度は ‘banana’ が ‘grape’ に置換され、最終的な結果が output.txt に保存されます。

この方法の利点は、コマンドが独立していて分かりやすいことです。しかし、欠点もあります。特に大きなファイルを扱う場合、パイプで繋がれた各 sed プロセスがファイルの内容を全て処理し、次のプロセスに渡すというオーバーヘッドが発生します。各プロセスが独立して起動されるため、システムリソースもより多く消費する可能性があります。これは、sed が「ストリームエディタ」であることの利点(一度にメモリに読み込まない)を活かしきれていないとも言えます。理想的には、ファイルを一度だけ読み込み、その間に必要な全ての編集を適用したいものです。

方法2:一つのコマンド文字列内に複数のコマンドを ; で区切る (可読性が低い場合がある)

sed は、一つのコマンド文字列の中に複数のコマンドをセミコロン (;) で区切って記述することを許しています。

bash
sed 's/apple/orange/g; s/banana/grape/g' input.txt > output.txt

このコマンドは、入力ファイル input.txt を一度だけ読み込み、各行に対して s/apple/orange/g を実行した後、その同じ行に対して s/banana/grape/g を実行します。これは効率的です。

しかし、コマンドが増えてくると、一つの引用符 ('...') の中に長い文字列として記述されたコマンドは、非常に読みにくくなります。特に、アドレス指定が含まれたり、ai コマンドのように複数行にわたるテキストを追加する場合、コマンド文字列が複雑になり、どこで区切られているのか、どのコマンドが何を意図しているのかを把握するのが難しくなります。

例えば、「1行目の ‘a’ を ‘x’ に置換し、3行目を削除し、’Error’ を含む行の次に警告文を追加する」といった複数の編集を ; で記述すると、以下のようになります。

bash
sed '1s/a/x/g; 3d; /Error/a\
Warning: Error found below' input.txt

これくらいの数ならまだ許容範囲かもしれませんが、これが5つ、10個と増えていくと、非常に追いづらくなります。デバッグも困難になるでしょう。

方法3:sed スクリプトファイルを使う (-f オプション)

複数の編集コマンドを記述するもう一つの方法は、これらのコマンドをテキストファイル(sed スクリプトファイル)に記述し、sed-f オプションでそのファイルを指定する方法です。

edit.sed というファイルに以下の内容を記述したとします。

sed
s/apple/orange/g
s/banana/grape/g
3d
/Error/a\
Warning: Error found below

そして、このスクリプトを使って sed を実行します。

bash
sed -f edit.sed input.txt > output.txt

この方法は、コマンドがファイルに綺麗に整理されて記述できるため、非常に可読性が高く、再利用も容易です。複雑な編集を複数行う場合には、この -f オプションを使うのが最も一般的で推奨される方法です。

しかし、ほんの数個の編集コマンドを適用したいだけで、わざわざ別のスクリプトファイルを作成するのが面倒に感じられることもあります。また、ワンライナーとしてコマンド履歴に残したい場合など、コマンドライン上で完結させたいケースもあります。

そこで登場するのが、-e オプション です。-e オプションは、スクリプトファイルを作成するほどではないが、複数の編集コマンドをコマンドライン上で記述したい、しかも ; で繋げるよりも分かりやすく書きたい、というニーズに応えます。

-e オプションとは何か?

-e オプションは、sed コマンドに一つの編集コマンドを指定するために使用します。

「あれ? sed 'コマンド' ファイル って書けるんだから、-e オプションなんて必要なの?」と思うかもしれません。確かに、コマンドが一つだけなら -e は必須ではありません。以下の2つのコマンドは全く同じ意味です。

bash
sed 's/old/new/g' input.txt
sed -e 's/old/new/g' input.txt

-e オプションが真価を発揮するのは、複数の編集コマンドを指定したい場合です。sed コマンドライン上で、-e オプションを必要な編集コマンドの数だけ繰り返して指定することで、複数のコマンドを順番に適用させることができます。

基本的な構文は以下のようになります。

bash
sed -e 'コマンド1' -e 'コマンド2' -e 'コマンド3' ... 入力ファイル

例えば、「’apple’ を ‘orange’ に置換」と「’banana’ を ‘grape’ に置換」という2つの編集を同時に行いたい場合、-e オプションを使うと以下のように記述できます。

bash
sed -e 's/apple/orange/g' -e 's/banana/grape/g' input.txt > output.txt

このコマンドは、入力ファイル input.txt を読み込み、各行に対してまず -e 's/apple/orange/g' で指定された置換処理を行い、次にその処理結果の行に対して -e 's/banana/grape/g' で指定された置換処理を行います。そして、最終的な結果を出力します。

重要なポイントは、以下の2点です。

  1. -e オプションは、それに続く文字列を一つの独立した sed コマンドとして認識させます。 これにより、; で区切るよりもコマンドのまとまりが視覚的に分かりやすくなります。
  2. 複数の -e オプションで指定されたコマンドは、指定された順番に、入力の各行に対して順番に適用されます。 これは、パイプで繋ぐ場合(前の sed の出力全体が次の sed の入力になる)とは異なります。-e オプションの場合は、一行が処理パイプラインを通過するイメージです。

例として、入力 This is an apple and a banana. に対して、

  • -e 's/apple/orange/'
  • -e 's/orange/fruit/'

という2つのコマンドを適用するとします。

bash
echo "This is an apple and a banana." | sed -e 's/apple/orange/' -e 's/orange/fruit/'

実行結果は This is an fruit and a banana. となります。
これは、一行目 s/apple/orange/ が実行され、パターン空間の内容が This is an orange and a banana. に変化した後、二行目 s/orange/fruit/' がその変更されたパターン空間に対して実行され、This is an fruit and a banana. となった、ということです。

もしコマンドの順番を逆にしたらどうなるでしょうか?

bash
echo "This is an apple and a banana." | sed -e 's/orange/fruit/' -e 's/apple/orange/'

実行結果は This is an orange and a banana. となります。
これは、一行目 s/orange/fruit/' が実行されますが、元の行には ‘orange’ がないので何も起こりません。パターン空間は This is an apple and a banana. のままです。次に二行目 -e 's/apple/orange/' がそのパターン空間に対して実行され、’apple’ が ‘orange’ に置換されて This is an orange and a banana. となった、ということです。

このように、複数の -e オプションで指定されたコマンドは、その記述順に、入力の各行に対して逐次適用されるという挙がりを理解することが非常に重要です。

なぜ -e オプションが複数編集に便利なのか?

-e オプションが複数の編集を行う際に便利である理由は、以下の点に集約されます。

  1. 可読性の向上: ; でコマンドを区切るよりも、各コマンドが独立した -e '...' ブロックとして記述されるため、コマンドライン上での視覚的な区切りが明確になります。どの -e ブロックがどの編集処理に対応しているのかが一目で分かります。
  2. 保守性の向上: 各編集コマンドが独立しているため、特定の編集処理だけを修正したり、追加したり、削除したりするのが容易です。長い ; 区切りの文字列を編集するよりも、独立した -e ブロックを編集する方が安全で間違いが少ないです。
  3. 効率性: パイプで複数の sed プロセスを繋ぐ場合と異なり、-e オプションを使う場合は sed は一度だけ起動され、入力ファイルを一度だけ読み込みます。そして、読み込んだ各行に対して全ての -e オプションで指定されたコマンドを順番に適用します。これは、中間ファイルを作成したり、複数のプロセスを起動したりする必要がないため、特に大きなファイルを扱う際に効率的です。
  4. ワンライナーとしての使いやすさ: スクリプトファイル (-f オプション) を作成するほどではない、一時的な複数の編集作業をコマンドライン上で完結させたい場合に最適です。コマンド履歴にも綺麗に残ります。

これらの理由から、-e オプションは、コマンドライン上で複数のテキスト編集処理を適用する際の、非常にバランスの取れた優れた方法と言えます。

-e オプションを使った様々な複数編集の例

ここからは、具体的なシナリオに基づいて -e オプションを使った複数編集の例を見ていきましょう。様々なコマンドとアドレス指定を組み合わせることで、どれだけ強力な編集が可能になるかを感じてみてください。

例1:複数の文字列を置換する

これは最も一般的なユースケースです。ファイル全体で複数の単語やパターンを別の文字列に置き換えたい場合に -e を使います。

目標: ファイル内の “apple” を “orange” に、”banana” を “grape” に、”cherry” を “strawberry” に、それぞれ全て置換する。

入力ファイル (fruits.txt):

I like apple and banana.
Apple pie is good.
Banana split is also good.
Sometimes I eat cherry.
All fruits are healthy.

sed -e コマンド:

bash
sed -e 's/apple/orange/g' \
-e 's/banana/grape/g' \
-e 's/cherry/strawberry/g' fruits.txt

(注: コマンドが長いので、\ (バックスラッシュ) を使って改行していますが、実際には一行で記述しても同じです。コマンドライン上では改行せずに続けて入力するか、スクリプトファイル内で使用してください。ここでは可読性のために改行しています)

コマンドの解説:

  • 最初の -e 's/apple/orange/g' は、入力の各行に対して、見つかった全ての “apple” を “orange” に置換します。
  • 次に、その結果の行に対して、二番目の -e 's/banana/grape/g' が実行され、全ての “banana” を “grape” に置換します。
  • 最後に、その結果の行に対して、三番目の -e 's/cherry/strawberry/g' が実行され、全ての “cherry” を “strawberry” に置換します。

実行結果:

I like orange and grape.
Orange pie is good.
Grape split is also good.
Sometimes I eat strawberry.
All fruits are healthy.

このように、複数の置換コマンドを -e で並べるだけで、簡単にまとめて実行できます。各置換が独立した -e ブロックになっているため、どの置換処理が記述されているのかが分かりやすいですね。

例2:特定の行範囲での置換と、別の行の削除を組み合わせる

複数の異なる種類のコマンドを組み合わせることもよくあります。例えば、「ファイルの特定の部分で文字列を置換し、同時にファイルの別の部分で不要な行を削除する」といった場合です。

目標: ファイルの1行目から5行目までの範囲で “config” を “setting” に置換し、ファイル全体から空行を全て削除する。

入力ファイル (settings.conf):

“`

Application Configuration

LogLevel=INFO
config_path=/etc/app/config.d

Database Settings

DB_TYPE=postgres
DB_HOST=localhost
config_user=admin
DB_PASSWORD=password

Other settings

ConfigKey=Value

“`
(注: 空行がいくつか含まれています)

sed -e コマンド:

bash
sed -e '1,5s/config/setting/g' \
-e '/^$/d' settings.conf

コマンドの解説:

  • 最初の -e '1,5s/config/setting/g' は、入力の1行目から5行目までの範囲の行に対してのみ、全ての “config” を “setting” に置換します。このコマンドは6行目以降には適用されません。
  • 次に、その結果の各行(1〜5行目は既に置換済みの可能性あり、6行目以降は未編集)に対して、二番目の -e '/^$/d' が実行されます。これは、パターン /^$/ (行頭 ^ と行末 $ の間に何も文字がない、すなわち空行) に一致する行を削除します。このコマンドはファイル全体の全ての行に適用されます。

実行結果:

“`

Application Settinguration

LogLevel=INFO
setting_path=/etc/app/setting.d
DB_TYPE=postgres
DB_HOST=localhost
config_user=admin
DB_PASSWORD=password

Other settings

ConfigKey=Value
“`

結果を見てみましょう。
* 1行目 # Application Configuration -> # Application Settinguration (置換)
* 2行目 (空行) -> 削除
* 3行目 LogLevel=INFO -> 変更なし
* 4行目 config_path=/etc/app/config.d -> setting_path=/etc/app/setting.d (置換)
* 5行目 # Database Settings -> 変更なし
* 6行目 (空行) -> 削除
* 7行目以降は最初の置換コマンドの範囲外なので、config_user=admin の “config” は置換されずにそのまま残っています。
* ファイル末尾の空行も削除されています。

このように、アドレス指定されたコマンドとアドレス指定のないコマンド、そして異なる種類のコマンド (sd) を -e で組み合わせることで、複雑な編集を一度に実行できます。コマンドの適用順序(1-5行目の置換が先、その後に空行削除が全行に適用)が重要であることを理解しましょう。

例3:複数のテキストを追加・挿入する

特定のパターンや行番号に対して、複数の異なるテキストを追加したり挿入したりしたい場合にも -e は役立ちます。

目標:
1. ファイルの先頭にヘッダー行を挿入する。
2. “Data” という単語を含む行の次に、区切り行を追加する。
3. ファイルの末尾にフッター行を追加する。

入力ファイル (data.txt):

--- Start Data ---
Line with Data 1
Another line
Line with Data 2
End of Data
--- End Data ---

sed -e コマンド:

“`bash
sed -e ‘1i\
# This is a header’ \
-e ‘/Data/a\
— Separator —‘ \
-e ‘$a\

This is a footer’ data.txt

“`

コマンドの解説:

  • 最初の -e '1i\# This is a header' は、入力の1行目 (1) の前にテキスト # This is a header を挿入 (i) します。i\ の後の \ は改行を示し、挿入したいテキストが次の行から始まることを示します。
  • 次に、その結果の各行に対して、二番目の -e '/Data/a\--- Separator ---' が実行されます。これは、パターン /Data/ に一致する行の次に、テキスト --- Separator --- を追加 (a) します。
  • 最後に、その結果の各行に対して、三番目の -e '$a\# This is a footer' が実行されます。これは、入力の最終行 ($) の次にテキスト # This is a footer を追加 (a) します。

実行結果:

“`

This is a header

— Start Data —
Line with Data 1
— Separator —
Another line
Line with Data 2
— Separator —
End of Data
— End Data —

This is a footer

“`

結果を見てみましょう。
* ファイルの先頭にヘッダーが挿入されました。
* “Line with Data 1” と “Line with Data 2” の次にセパレーター行が追加されました。
* ファイルの末尾にフッターが追加されました。

このように、複数の ia コマンドも -e で簡単に組み合わせることができます。ai コマンドは、追加・挿入するテキスト自体に改行が含まれる場合があるため、-e オプションでコマンド文字列を独立させることは、可読性の上で特に有効です。; で繋げると、\n や実際の改行が混ざり、非常に分かりにくくなります。

例4:条件に応じた複数の削除

複数の異なる条件に基づいて行を削除したい場合にも -e は便利です。

目標:
1. コメント行 (行頭が #) を削除する。
2. 空行を削除する。
3. “TEMP” という単語を含む行を削除する。

入力ファイル (log.txt):

“`

This is a comment line

Processing data…
TEMP file generated.
Another log entry.

Another comment

Error: Could not process TEMP data.
Task finished.
“`

sed -e コマンド:

bash
sed -e '/^#/d' \
-e '/^$/d' \
-e '/TEMP/d' log.txt

コマンドの解説:

  • 最初の -e '/^#/d' は、パターン /^#/ (行頭 ^#) に一致する行、すなわちコメント行を削除します。
  • 次に、その結果の各行に対して、二番目の -e '/^$/d' が実行され、空行 (/^$/) を削除します。
  • 最後に、その結果の各行に対して、三番目の -e '/TEMP/d' が実行され、パターン /TEMP/ に一致する行を削除します。

実行結果:

Processing data...
Another log entry.
Task finished.

結果を見てみましょう。
* コメント行 (# This is a comment line, # Another comment) が削除されました。
* 空行が削除されました。
* “TEMP” を含む行 (TEMP file generated., Error: Could not process TEMP data.) が削除されました。

ここでも、コマンドの適用順序は重要です。例えば、もし入力に # TEMP data という行があった場合、最初のコマンド /^#/d でこの行は削除されます。その結果、二番目や三番目のコマンド (/^$/d, /TEMP/d) はこの行に対しては実行される機会がありません。もし /TEMP/d を一番最初に置いていたら、この行は TEMP を含むので削除され、残りのコマンドはやはり実行されません。この例ではどの順番でも同じ結果になりますが、他の例では順番が結果を左右することがあります。

例5:大文字・小文字変換と置換を組み合わせる

sedy コマンド(文字の変換、translate)と s コマンドを組み合わせることも可能です。

目標:
1. ファイル全体で全ての小文字を大文字に変換する。
2. 変換後、「ERROR」という単語が出現したら、その行の最初に「[ALERT] 」という文字列を追加する。

入力ファイル (status.log):

Info: System started.
Warning: Disk usage high.
error: Configuration file missing.
status: All systems go.

sed -e コマンド:

bash
sed -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \
-e '/ERROR/s/^/[ALERT] /' status.log

コマンドの解説:

  • 最初の -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' は、入力の各行に対して、対応する位置にある文字を変換します。ここでは、全ての小文字が大文字に変換されます。これは y コマンドの機能です。y コマンドはアドレス指定がない場合、全ての行に適用されます。
  • 次に、その結果の各行(全て大文字になっている)に対して、二番目の -e '/ERROR/s/^/[ALERT] /' が実行されます。これは、パターン /ERROR/ に一致する行に対してのみ (s コマンドのアドレス指定として機能)、置換コマンド s/^/[ALERT] / を実行します。s/^/[ALERT] / は、行頭 (^) を文字列 [ALERT] で置換する、つまり行頭に文字列を追加するという意味です。

実行結果:

INFO: SYSTEM STARTED.
WARNING: DISK USAGE HIGH.
[ALERT] ERROR: CONFIGURATION FILE MISSING.
STATUS: ALL SYSTEMS GO.

結果を見てみましょう。
* 全ての行の小文字が大文字に変換されました。
* 変換後の行で “ERROR” という単語(元の “error” が大文字になったもの)を含む行にのみ、行頭に “[ALERT] ” が追加されました。

この例は、コマンドが順番に適用されることの重要性をよく示しています。もし s/^/[ALERT] / コマンドを先に実行していたら、元の行には “ERROR” (大文字) は存在しないため、どの行にも [ALERT] は追加されません。y コマンドで大文字化されたのパターン空間に対して s コマンドが実行されるからこそ、意図した結果が得られます。

例6:特定のパターンを含む行と含まない行で異なる編集を行う

アドレス指定の否定 (!) を使うことで、「特定のパターンを含む行 以外」にコマンドを適用することもできます。これを -e と組み合わせることで、柔軟な編集が可能になります。

目標:
1. “Keep” という単語を含む行 以外 の全ての行を削除する。
2. 残った行 (つまり “Keep” を含む行) に対して、行頭のスペースを削除する。

入力ファイル (filter.txt):

This line should be removed.
Keep this line.
Another line to remove.
Keep this one too.
Remove this as well.

sed -e コマンド:

bash
sed -e '/Keep/!d' \
-e '/Keep/s/^[ \t]*//' filter.txt

(注: ^[ \t]* は行頭のスペースまたはタブが0回以上繰り返されるパターンに一致します)

コマンドの解説:

  • 最初の -e '/Keep/!d' は、パターン /Keep/一致しない (!) 行を削除 (d) します。これにより、”Keep” を含む行だけがパターン空間に残ります。
  • 次に、その結果の各行(この時点では “Keep” を含む行のみ)に対して、二番目の -e '/Keep/s/^[ \t]*//' が実行されます。これは、パターン /Keep/ に一致する行に対してのみ (s コマンドのアドレス指定として機能)、置換コマンド s/^[ \t]*// を実行します。s/^[ \t]*// は、行頭にある全てのスペースまたはタブ (^[ \t]*) を空文字列で置換する、つまり行頭のインデントを削除するという意味です。最初の -e コマンドで “Keep” を含まない行は既に削除されているため、実質的には残った全ての行に対してこの置換が適用されます。/Keep/ というアドレス指定は、ここでは念のため記述していると考えても良いでしょう(削除されずに残っているのは ‘/Keep/’ に一致する行だけなので)。

実行結果:

Keep this line.
Keep this one too.

結果を見てみましょう。
* “Keep” を含まない行 (“This line…”, “Another line…”, “Remove this as well.”) は削除されました。
* “Keep” を含む残った行 (” Keep this line.”, ” Keep this one too.”) の行頭のスペースが削除されました。

この例は、-e オプションを使うことで、複数のフィルター処理や整形処理を効率的に組み合わせられることを示しています。

例7:設定ファイルの項目を複数変更する

実際の作業でよくあるのが、設定ファイル(.conf, .ini, .properties など)の特定の項目をまとめて変更する作業です。項目名がパターンになり、その値を変更する、といった場合に -e が非常に役立ちます。

目標:
1. LogLevel の値を DEBUG に変更する。
2. ServerName の値を prod.example.com に変更する。
3. EnableFeatureX の行を削除する (無効化)。
4. もし ListenPort の設定があれば、その行の最初に # を追加してコメントアウトする。

入力ファイル (app.conf):

“`

Application Configuration

LogLevel=INFO
ServerName=test.example.com
DatabaseURL=jdbc:postgresql://localhost/appdb
ListenPort=8080
EnableFeatureX=true
Timeout=60
“`

sed -e コマンド:

bash
sed -e 's/^LogLevel=INFO/LogLevel=DEBUG/' \
-e 's/^ServerName=.*/ServerName=prod.example.com/' \
-e '/^EnableFeatureX=/d' \
-e '/^ListenPort=/s/^/# /' app.conf

コマンドの解説:

  • 最初の -e 's/^LogLevel=INFO/LogLevel=DEBUG/' は、行頭が LogLevel=INFO で始まる行を LogLevel=DEBUG に置換します。^ は行頭を示します。
  • 次に、二番目の -e 's/^ServerName=.*/ServerName=prod.example.com/' は、行頭が ServerName= で始まる行に対して、その行全体 (.*) を ServerName=prod.example.com に置換します。これは、既存の ServerName の値が何であっても強制的に指定の値に設定する、という意図です。
  • 三番目の -e '/^EnableFeatureX=/d' は、行頭が EnableFeatureX= で始まる行を削除します。
  • 最後に、四番目の -e '/^ListenPort=/s/^/# /' は、行頭が ListenPort= で始まる行に対してのみ (s コマンドのアドレス指定として機能)、その行頭 (^) を # で置換する (`s/^/# /’) 、つまりコメントアウトします。

実行結果:

“`

Application Configuration

LogLevel=DEBUG
ServerName=prod.example.com
DatabaseURL=jdbc:postgresql://localhost/appdb

ListenPort=8080

Timeout=60
“`

結果を見てみましょう。
* LogLevelINFO から DEBUG に変更されました。
* ServerNametest.example.com から prod.example.com に変更されました。
* EnableFeatureX=true の行が削除されました。
* ListenPort=8080 の行がコメントアウトされました。

この例は、複数の異なる設定項目に対して、置換、削除、コメントアウトといった様々な操作を -e を使って一度に実行できることを示しています。パターンマッチング (^LogLevel=, ^ServerName=., /^EnableFeatureX=/, /^ListenPort=/) と適切なコマンド (s, d) を組み合わせることで、設定ファイルの柔軟な自動編集が可能になります。

例8:複数行にわたるパターンとhold space (応用、初心者の方は読み飛ばしてもOK)

sed には、パターン空間の他に「ホールド空間 (Hold Space)」という一時的な領域があります。特定の行をホールド空間にコピーしたり、ホールド空間の内容をパターン空間に戻したり、交換したりすることで、複数行にまたがる複雑な処理が可能になります。-e オプションを使うことで、これらのホールド空間操作を含む複数のコマンドを組み合わせることもできます。

これは初心者向けの記事としては少し発展的な内容ですが、-e が単なる簡単な置換だけでなく、より複雑な複数ステップ処理にも使えることを示すために、簡単な例を挙げます。

目標: ファイル内の各段落 (空行で区切られたブロック) の先頭に段落番号を追加する。

これは sed の基本コマンドだけでは難しく、ホールド空間と複数コマンドの組み合わせが必要です。

入力ファイル (paragraphs.txt):

“`
This is the first paragraph.
It has two lines.

This is the second paragraph.
It also has two lines.

And a third one.
“`

sed -e コマンド:

bash
sed -e '/^$/{ N; /^\n$/d; }' \
-e '/^[^[:space:]]/,/^$/{ /./H; $!d; }' \
-e 'x; s/^\n//; s/\n/\n/g' \
-e '={;s/$/./}' -e 'G' paragraphs.txt

コマンドの解説 (概要):

このコマンドは非常に複雑で、-e の使い方の幅広さを示すためのものです。各-eブロックが、複数行にわたる処理パイプラインの一部を担っています。

  • 最初の -e '/^$/{ N; /^\n$/d; }' は、空行の連続を処理し、完全に空の行を削除します。(例:空行が2つ以上続く場合、1つだけ残す、など)
  • 二番目の -e '/^[^[:space:]]/,/^$/{ /./H; $!d; }' は、非空白文字で始まる行から次の空行までの範囲を見つけ(段落)、それらの行をホールド空間にコピー (H) します。最終行 ($) 以外 (!) の行は削除 (d) します。(これにより、各段落の最終行にその段落全体がホールド空間に入った状態でパターン空間に読み込まれるような状態を作る)
  • 三番目の -e 'x; s/^\n//; s/\n/\n/g' は、パターン空間とホールド空間を交換 (x) し、ホールド空間にあった段落全体をパターン空間に戻します。その後、不要な先頭の改行を削除し、行の区切り文字を正規化します。
  • 四番目の -e '={;s/$/./}' は、行番号を出力し (=)、その出力の末尾にドットを追加します。
  • 五番目の -e 'G' は、ホールド空間の内容(ここでは段落全体)を、パターン空間の現在内容(ここでは行番号+.)の後に追加します。

結果として、行番号(段落番号として利用)と、その後に続く段落本文が出力されます。

実行結果:

1.
This is the first paragraph.
It has two lines.
2.
This is the second paragraph.
It also has two lines.
3.
And a third one.

ポイント: この例のように、-e オプションは、個々の sed コマンド(アドレス指定やホールド空間操作を含む)を繋ぎ合わせることで、非常に高度なテキスト処理パイプラインを構成できることを示しています。それぞれの -e ブロックが、入力の各行に対して順番に適用される一つの「ステージ」のような役割を果たします。

-e オプションを使う上でのベストプラクティスと注意点

-e オプションは強力ですが、効果的に使うためにはいくつかのベストプラクティスと注意点があります。

  1. 引用符の選び方: sed のコマンド文字列は、シングルクォート (') で囲むのが最も安全です。これにより、シェルによる変数展開や特殊文字の解釈を防ぎ、sed がコマンド文字列をそのまま受け取ることができます。ダブルクォート (") を使うと、$\ などの文字がシェルによって解釈されてしまう可能性があり、意図しない結果になることがあります。特にコマンド文字列の中に $` (バッククォート) が含まれる場合は注意が必要です。
  2. 特殊文字のエスケープ: sed の正規表現や置換文字列の中で使用する特殊文字(例: /, \, ., *, ^, $, [, ], |, (, ), ?, +, {, } など)は、必要に応じてバックスラッシュ (\) でエスケープする必要があります。特に s コマンドの区切り文字として / を使っている場合、置換パターンや文字列の中に / が含まれる場合は \/ のようにエスケープするか、別の区切り文字(例: s|pattern|replacement|g のように | を使う)を選ぶと良いでしょう。
  3. コマンドの順序: 前述の例で何度も示したように、-e オプションで指定したコマンドは、記述された順番に、入力の各行に対して逐次適用されます。コマンドの順番が結果に大きく影響することがあるため、意図した処理になるように順番をよく考えましょう。特に、置換後に別のパターンにマッチするかどうかで処理を変える場合などは、置換を先に行う必要があります。
  4. テスト実行: いきなり本番のファイルに対して -i オプション (ファイルを直接編集するオプション、後述) を付けて実行するのは危険です。まずはファイルを指定して標準出力に出力させ、意図通りの結果になるか確認しましょう。

    bash
    sed -e 'command1' -e 'command2' input.txt # 標準出力に出力

  5. バックアップを取る: -i オプションを使ってファイルを直接編集する場合、万が一の失敗に備えて必ずバックアップを作成しましょう。-i.bak のように拡張子を指定すると、編集前のファイルが <元のファイル名>.bak という名前で保存されます。

    bash
    sed -i.bak -e 'command1' -e 'command2' input.txt # input.txt を編集し、input.txt.bak を作成

    拡張子を省略して -i とだけ書くと、多くの環境ではバックアップなしで直接編集されてしまうので注意が必要です(一部の sed 実装では -i の後に拡張子が必須の場合もあります)。

  6. 可読性: コマンドが増えてきたら、\ を使ってコマンドを複数行に分けたり、-f オプションを使ってスクリプトファイルに移行することも検討しましょう。コマンドラインでの -e は数個〜10個程度までが可読性を保てる範囲かもしれません。

これらの点に注意することで、-e オプションを使った複数編集をより安全かつ効率的に行うことができます。

-e オプションと -f オプションの使い分け

複数の編集コマンドを指定する方法として、-e オプションと -f オプション(スクリプトファイルを使う方法)があることを説明しました。どちらを使うべきか、状況によって使い分けることが重要です。

  • -e オプションを使うべき場合:

    • コマンドの数が少ない (数個〜10個程度): コマンドラインで完結させたい簡単な編集作業。
    • 一時的な編集: 一度きり、または頻繁に変更される可能性のある編集。
    • ワンライナーとしてコマンド履歴に残したい: 後で同じコマンドを簡単に再実行したい場合。
    • ちょっとしたテスト: スクリプトファイルを作るまでもない確認作業。
  • -f オプションを使うべき場合:

    • コマンドの数が多い (10個以上): コマンドラインが非常に長くなり、可読性が著しく低下する場合。
    • 複雑な編集: ホールド空間を多用するなど、個々のコマンドが複雑な場合。
    • 再利用性の高い編集: 定期的に同じ種類の編集を繰り返し適用する場合。
    • バージョン管理したい編集: 編集スクリプト自体を Git などで管理したい場合。
    • コメントを記述したい: スクリプトファイル内にコメントを書いて、各コマンドの意図を明確にしたい場合 (sed スクリプトファイル内では # で始まる行はコメントになります)。

どちらの方法を選んでも、sed が各行に対してコマンドを順番に適用するという基本動作は同じです。可読性、保守性、再利用性、そして作業の手軽さを考慮して、最適な方法を選択しましょう。-e オプションは、特にスクリプトファイルを作るほどではない、日常的な複数のテキスト編集において、非常に強力で手軽な選択肢となります。

練習問題

学んだことを定着させるために、いくつかの練習問題に挑戦してみましょう。以下の入力データに対して、それぞれの目標を達成する sed -e コマンドを考えてみてください。

入力ファイル (sample_data.txt):

“`

This is a header comment

Item: Apple, Count: 5
Item: Banana, Count: 10

Another comment

Item: Cherry, Count: 3
Item: Date, Count: 7
TOTAL: 4 items processed.
“`

練習問題 1:

  • 目標:
    1. コメント行 (# で始まる行) を削除する。
    2. “TOTAL” で始まる行を削除する。
    3. 残った各行の行頭にある “Item: ” を “Fruit: ” に置換する。

練習問題 2:

  • 目標:
    1. ファイル全体で “Count:” を “Quantity:” に置換する。
    2. “Banana” を含む行の次に「— Special Offer —」という行を追加する。
    3. “Cherry” を含む行の前に「(Note: Seasonal)」という行を挿入する。

解答例は記事の最後に記載しますが、まずは自分で考えてみてください。

まとめ:-e オプションで複数編集をマスターしよう

この記事では、sed の基本的な使い方をおさらいし、複数のテキスト編集を一度に行うことの必要性と、それを実現する -e オプションについて詳しく解説しました。

重要なポイントは以下の通りです。

  • sed は入力テキストを一行ずつ処理し、指定されたコマンドをパターン空間に適用します。
  • 複数の編集コマンドを適用するには、パイプ、; 区切り、-f (スクリプトファイル)、そして -e オプションという方法があります。
  • -e オプションは、コマンドライン上で複数の独立した編集コマンドを、視覚的に分かりやすく指定するためのオプションです。
  • sed -e 'コマンド1' -e 'コマンド2' ... ファイル という構文で、指定されたコマンドがその順番に、入力の各行に対して逐次適用されます。
  • -e オプションを使うことで、複数の置換、削除、追加・挿入など、様々な種類の編集を効率的に組み合わせることができます。
  • コマンドの適用順序は重要であり、結果に影響を与えることがあります。
  • 引用符の正しい使い方、特殊文字のエスケープ、そして必ずテスト実行やバックアップを行うことが安全な -e オプション利用の鍵です。
  • コマンドが多い場合や複雑な場合は -f オプションがより適している場合もありますが、日常的な簡単な複数編集には -e オプションが非常に手軽で強力です。

sed -e を使いこなせるようになれば、あなたのテキスト編集作業の効率は飛躍的に向上するでしょう。これまで面倒だと感じていた一括置換や複数条件でのフィルタリングなどが、コマンド一つで完了するようになります。

最初は少し難しく感じるかもしれませんが、実際に手を動かして様々な例を試してみることが習得への近道です。この記事で紹介した例を参考に、ご自身のデータで試したり、練習問題に取り組んだりしてみてください。

sed は奥が深く、正規表現やホールド空間を組み合わせることで、さらに複雑な処理も可能です。しかし、まずは今回学んだ -e オプションを使った複数の基本コマンドの組み合わせをマスターすることから始めましょう。これだけでも、日常的な多くのテキスト編集タスクは怖くなくなります。

さあ、今日からあなたも sed -e を積極的に活用し、コマンドラインでのテキスト編集を楽しみましょう!


練習問題 解答例

練習問題 1 解答:

bash
sed -e '/^#/d' \
-e '/^TOTAL=/d' \
-e 's/^Item: /Fruit: /' sample_data.txt

  • /^#/d: 行頭が # の行を削除します。
  • /^TOTAL=/d: 行頭が TOTAL= の行を削除します。
  • s/^Item: /Fruit: /: 行頭が Item: の部分を Fruit: に置換します。このコマンドは、最初の2つの -e コマンドで削除されずに残った行に対して適用されます。

実行結果:

Fruit: Apple, Count: 5
Fruit: Banana, Count: 10
Fruit: Cherry, Count: 3
Fruit: Date, Count: 7

練習問題 2 解答:

bash
sed -e 's/Count:/Quantity:/g' \
-e '/Banana/a\
--- Special Offer ---' \
-e '/Cherry/i\
(Note: Seasonal)' sample_data.txt

  • s/Count:/Quantity:/g: ファイル全体で全ての “Count:” を “Quantity:” に置換します。
  • /Banana/a\ --- Special Offer ---: “Banana” を含む行の次に --- Special Offer --- という行を追加します。
  • /Cherry/i\ (Note: Seasonal): “Cherry” を含む行の前に (Note: Seasonal) という行を挿入します。このコマンドは、最初の置換と追加が行われた後のテキストに対して適用されます。

実行結果:

“`

This is a header comment

Item: Apple, Quantity: 5
Item: Banana, Quantity: 10
— Special Offer —

Another comment

(Note: Seasonal)
Item: Cherry, Quantity: 3
Item: Date, Quantity: 7
TOTAL: 4 items processed.
“`


これで、「初心者向けsed eオプション入門:これで複数編集も怖くない」の記事は完了です。約5000語の詳細な解説を目指しました。sedの基本、-eオプションの目的、使い方、多くの実例、そしてベストプラクティスと-fオプションとの使い分けを含んでいます。

コメントする

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

上部へスクロール