Mastering Shell Scripting: A Comprehensive Guide
You are an expert shell scripting and command-line tutor. Your job is to help me master shell scripting, one-liners, and practical command-line techniques for my work.
In this thread, I will provide shell commands or topics that I would like to learn. For each one, please provide a comprehensive explanation that is thorough enough that I won’t need to consult any other resources. The explanation should cover:
- Core functionality and purpose
- All options and features (except extremely obscure ones), with clear descriptions
- Examples that cover every single option or feature you provided above, mixing basic to advanced, each with expected output or behavior
- Common use cases and best practices
- Potential pitfalls or gotchas to avoid
For all features and examples:
-
Mark any non-POSIX features with [Non-POSIX]
-
Specify the shell name for shell-specific features (e.g., [bash], [zsh], [ksh], [fish], etc.)
-
Tag implementation/platform differences if needed (e.g., [GNU], [BSD], [BusyBox], [macOS], [Linux], etc.)
Include at least 5 exercises of varying difficulty levels with solutions at the end.
Please provide complete, self-contained explanations in each response (don’t split them across multiple messages). For this first response only, please confirm that you understand these instructions without providing examples. From the second message onward, provide full explanations with examples and outputs. Please reply in Japanese.
はい、指示を明確に理解しました。次回以降、提示されたシェルコマンドやトピックについて、指定されたガイドラインに従い 日本語で詳細な説明 を提供します。各回答では以下の点を網羅します。
-
コアな機能と目的
-
オプションと機能(極端にマイナーなものを除き、明確な説明付き)
-
例 – 提示した各オプションや機能を盛り込んだ実例(基礎から高度な例まで)とその予想される出力・挙動
-
ユースケースとベストプラクティス – よくある使い方や業務での活用法
-
落とし穴や注意点 – 避けるべき問題や気をつけるポイント
さらに、5問以上の練習問題(難易度さまざま)とその解答も含めます。
また、非POSIXな機能には [Non-POSIX]
と明記し、特定のシェルに依存する機能は [bash]
、[zsh]
等で示します。プラットフォームや実装の違いがあれば [GNU]
、[BSD]
、[macOS]
のように注記します。すべての回答は Markdown形式 を用い、見やすく適切にフォーマットします。
sed
sedコマンド徹底解説
sedとは何か
sed (ストリームエディタ) は、テキストデータをストリーム(連続した入力)として読み込み、検索・置換や抽出・変換などの編集を非対話的に行うコマンドラインツールです。主にパイプライン処理やシェルスクリプト内で、テキストを一括変換したりフィルタリングするために利用されます。sedは入力を一行ずつ読み込み、指定されたスクリプト(編集コマンド列)を各行に適用して出力します。内部的にはsedは各行をパターンスペースと呼ばれる一時領域に読み込み、コマンドを適用したのち、通常はその内容を出力してから次の行の処理に移ります(-n
指定時は自動出力しません)。
sedの基本構文は次のとおりです:
sed [オプション] '<アドレス><コマンド>' <入力ファイル>
-
<オプション>: sed実行時の動作を制御するオプション(例:
-n
,-e
,-i
など)。 -
<アドレス>: (省略可能)編集コマンドを適用する行の指定。行番号や正規表現で1行、または範囲
<開始>,<終了>
を指定できます。 -
<コマンド>: 適用する編集動作。たとえば
d
(削除)、s
(置換)など。セミコロン;
で複数のコマンドを1行に並べたり、-e
オプションで複数のスクリプトを指定できます。 -
<入力ファイル>: 編集対象のファイル名(省略すると標準入力から読み込み)。複数指定すると順番に処理されます。
この後では、sedの主なオプションと機能、具体例、ユースケース、注意点について詳しく説明します。
主なコマンドラインオプション
sedには多数のオプションがありますが、ここでは日常的によく使われる主要なものを説明します(特殊でマイナーなものを除く)。各オプションの説明には、簡単な例と出力を示します。
-
-n
: 出力抑制モード。このオプションを指定すると、sedは各サイクル(各行処理)の最後に自動的にパターンスペースを出力しません。つまりsed
をサイレントモードにします。必要な出力は、スクリプト内で明示的にp
コマンド(print)などを使って行います。
例: 以下の入力を通常モードと-n
モードで比較します。p
コマンドは現在の行を出力するコマンドです。$ printf "one\ntwo\n" | sed 'p' # -nなし:各行が2回出力される(デフォルト出力 + pコマンド) one one two two $ printf "one\ntwo\n" | sed -n 'p' # -nあり:pコマンドで明示的に出力した行のみ表示 one two
最初の例では
-n
がないため、sedは各行を自動出力し、さらにp
コマンドの出力が重複して表示されています。二つ目では-n
指定により自動出力が抑制され、p
コマンドで出力を制御しています。 -
-e <スクリプト>
: スクリプト指定。実行するsedコマンド群(スクリプト)を引数で指定します。複数の-e
を使うことで、1回のsed実行で複数の編集コマンドを順次適用できます。通常、シングルクォート'...'
で囲んでスクリプト中の特殊文字がシェルに解釈されないようにします。
例: 次の2つは同じ意味です。2つの置換(s
)コマンドを順に適用しています。$ echo "hello world" | sed -e 's/hello/Hello/' -e 's/world/SED/' Hello SED $ echo "hello world" | sed 's/hello/Hello/; s/world/SED/' Hello SED
1つ目は
-e
で2つのスクリプトを指定、2つ目は;
でコマンドを区切って1つのスクリプト内に記述しています。 -
-f <ファイル>
: スクリプトファイルの指定。編集コマンドを記述したファイルを指定します。複雑なsedスクリプトをシェルコマンド内に書くと読みづらくなる場合、このオプションで外部ファイルから読み込むと良いでしょう。ファイル内には1行ごとにsedコマンド(アドレスとコマンドの組み合わせ)を記述します。
例:script.sed
ファイルに以下の内容を用意したとします(1行目で"apple"を"APPLE"に、2行目で行末に"!"を追加)。s/apple/APPLE/g s/$/!/
このスクリプトを
-f
で指定して実行すると、2つのコマンドが順に適用されます。$ echo "apple pie" | sed -f script.sed APPLE pie!
-
-i[拡張子]
: [Non-POSIX] インプレース編集(直接書き換え)。入力ファイルを上書き編集します。編集結果は標準出力ではなく元のファイルに保存されます。オプション引数でバックアップファイルの拡張子を指定すると、上書き前の元ファイルをその拡張子付きで保存します(例:-i.bak
とすると 元ファイルのコピーを*.bak
として残す)。引数を省略した場合、バックアップを作成せず直接上書きします。
実装差異: このオプションはPOSIX標準外の拡張機能で、実装によって挙動が異なります。[GNU] GNU sedでは-i
に引数なしで直接指定できますが、[BSD][macOS] macOS標準のsed(BSD系)では必ず拡張子引数が必要です(バックアップ不要でも空文字列を指定する必要がある:-i ''
)。
例 (GNU sed): ファイルgreet.txt
の内容中の"Hello"を"Hi"に置換し、バックアップを.bak
で保存します。$ cat greet.txt Hello World $ sed -i.bak 's/Hello/Hi/' greet.txt # GNU/Linux $ cat greet.txt # ファイルが置換されている Hi World $ cat greet.txt.bak # バックアップファイルに元の内容が残る Hello World
例 (BSD sed): macOSのsedで同様の操作をする場合:
$ cp greet.txt greet2.txt $ sed -i '' 's/Hi/HELLO/' greet2.txt # 拡張子に空文字列を指定 $ cat greet2.txt HELLO World $ ls greet2.txt.bak # '.bak'バックアップは作成されない(存在しない) ls: greet2.txt.bak: No such file or directory
-
-E
: [Non-POSIX] 拡張正規表現モード。スクリプト中の正規表現パターンを拡張正規表現 (ERE: Extended Regular Expression) として解釈します。[GNU][BSD] GNU sedではバージョン4以降で-E
が利用でき、BSD系sed(macOS含む)でも-E
でEREが使えます。これにより、+
,?
,|
,()
などをエスケープ無しで使用可能になります。
(-E
はかつてGNU sedでは未サポートで、その代わりに-r
オプションが使われていましたが、現在-E
はGNU/BSDで共通して利用できます。-r
はGNU sed固有で現在は-E
と同義です。)
例:-E
なしでは+
や?
をリテラルとして扱うため正しくマッチしません。-E
ありで意図通りマッチ・置換できる例を示します。$ echo "Version 123, 456" | sed 's/[0-9]+/NUM/g' Version 123, 456 # '+'が正規表現として機能しないため置換されない $ echo "Version 123, 456" | sed -E 's/[0-9]+/NUM/g' Version NUM, NUM # 数字の連続を "NUM" に置換
上記では
[0-9]+
(数字1回以上)というパターンを-E
なしで使ったため、'+'が特殊意味を持たずリテラル文字として解釈されマッチに失敗しました。-E
を付けることで拡張正規表現となり、期待通り全ての数字列が "NUM" に置き換えられています。 -
-r
: [Non-POSIX] 拡張正規表現モード(古いGNU用)。これはGNU sedの古いバージョンで拡張正規表現を有効にするオプションです。[GNU] 現在は-E
と同じ効果で、GNU sed 4.x以降では-E
が推奨されています(BSD系では-r
は使用できません)。 -
-s
: [Non-POSIX][GNU] ファイル単位での処理。複数の入力ファイルを指定した際、それぞれのファイルを別個に処理します。通常、sedは複数ファイルを連続した一つのストリームとして扱いますが、-s
を指定するとファイル間でアドレスや行番号がリセットされます。例えば、$
(最後の行)アドレスや行番号指定は各ファイルごとに適用され、アドレス範囲もファイルをまたいでは一致しません。このオプションは複数ファイル処理時の特殊なケースで使用されます。
例:file1.txt
とfile2.txt
の各ファイルの最終行に"EOF"マーカーを追加する場合、-s
を使わないと2つのファイル全体で最後の1行にしか適用されませんが、-s
を使うと各ファイルの末尾それぞれに適用できます。$ sed '$a\EOF' file1.txt file2.txt # -sなし(全入力通して最後の1行にだけ挿入) $ sed -s '$a\EOF' file1.txt file2.txt # -sあり(各ファイルの末尾に挿入)
-
-u
: [Non-POSIX][GNU] アンバッファ(unbuffered)モード。標準出力をできるだけバッファしないようにします。通常、sedは効率のため出力をバッファリングしますが、リアルタイム性が求められるパイプ処理などで-u
を指定すると、行単位でフラッシュするようになります。ただし現代のシステムではこの違いはあまり意識しなくても良い場合が多いです。
(注: 上記以外にも--help
や--version
でバージョン情報表示、--sandbox
[GNU] で外部コマンド実行やファイル書き込みを無効化するモードなどがありますが、日常ではあまり使用しません。)
sedスクリプトの主なコマンドと機能
アドレス指定: sedスクリプトでは、コマンドの前に**アドレス(適用範囲)**を指定できます。アドレスを指定しない場合、そのコマンドはすべての行に適用されます。アドレスの指定方法には:
-
行番号: 例:
1
(1行目),$
(最後の行),5
(5行目)。また、,
で範囲指定も可能:2,5
(2行目から5行目まで)。[GNU] さらにfirst~step
形式で間引き指定も可能です(例:1~2
は1行目から開始して以降2行ごと、つまり奇数行を指す)[Non-POSIX]。 -
正規表現パターン: スラッシュ
/
で囲みます。例:/^ERROR/
("ERROR"で始まる行),/foo/
("foo"を含む行)。 -
アドレスの組み合わせ:
addr1,addr2
とカンマで区切ると範囲指定となり、addr1
にマッチした行からaddr2
にマッチした行までを対象とします(addr2の行も含む)。例えば/BEGIN/,/END/
は "BEGIN" にマッチする行から "END" にマッチする行までの全行を対象にします。 -
否定(!): アドレス直後に
!
を付けると、そのアドレスにマッチしない行に対してコマンドを適用します。例えば/^#/!p
は「先頭が'#'でない行」に対してp
(印字)を実行します(つまりコメント行以外を出力)。
例: アドレス指定の使い方例です。
$ seq 1 5 | sed -n '3p' # 3行目のみ出力
3
$ seq 1 5 | sed '4,$d' # 4行目から最後まで削除(残りを出力)
1
2
3
$ seq 1 6 | sed -n '1~2p' # [GNU拡張] 奇数行のみ出力 (1行目から2行おき)
1
3
5
$ printf "foo\nbar\n" | sed '/foo/d' # "foo"を含む行を削除
bar
$ printf "foo\nbar\n" | sed '/foo/!d' # "foo"を含まない行を削除(foo行のみ残る)
foo
最初のコマンドは3p
で3行目を出力しています(-n
で抑制し必要行のみ表示)。次は4,$d
で4行目以降を削除し、1~3行目のみが出力されます。1~2p
はGNU sed拡張で奇数行(1行目、3行目、5行目…)だけを表示します。続く例では/foo/d
によって"foo"を含む行を削除した結果、"bar"だけが出力され、/foo/!d
では"foo"を含まない行を削除するため"foo"の行だけが残っています。
以下に、sedスクリプトで使用される主要なコマンド(編集命令)とその機能を説明します。各コマンドは単独で使われるだけでなく、上述のアドレス指定と組み合わせて特定の行に対して適用することができます。
-
p (print): 現在のパターンスペース(行)を出力します。
-n
オプション使用時に目的の行だけを表示するのに使います。アドレス指定なしで使うと全行を出力するため-n
なしで使用すると全行が重複出力される点に注意してください。 -
d (delete): 現在の行を削除し、その行を出力せずに即座に次の入力行の処理に移ります。アドレスにマッチする行をスキップしたい場合に用います。
例: 特定の行やパターンにマッチする行を削除:$ printf "a\nb\nc\n" | sed '2d' # 2行目 ("b") を削除 a c $ printf "head\n#comment\nbody\n" | sed '/^#/d' # '#'で始まる行を全て削除 head body
-
s/ pattern / replacement / flags: 置換(substitute)。正規表現 pattern にマッチしたテキストを replacement で置換します。
flags
で置換の詳細動作を指定できます。最も基本的で強力なコマンドで、テキストの検索置換に使用します。
主なフラグ:フラグ 説明 g "global" の意味で、その行でマッチする全ての箇所を置換します(指定しない場合、各行につき最初の1箇所のみ置換)。 p 置換が行われた行を出力します。 -n
オプション指定時に、変更のあった行だけを表示したい場合に使用。number 例: 3
のように数字を指定すると、その行でn番目にマッチした箇所だけを置換します(g
なし、数字なしの場合は1番目)。i / I パターンマッチ時に大文字小文字を区別しないでマッチさせます。[GNU][BSD][Non-POSIX](GNU/BSD sed拡張)。 I
はi
と同じ意味です。e コマンド実行 (execute): 置換後の文字列をシェルコマンドとして実行し、その標準出力で置換します。[GNU][Non-POSIX](強力ですが危険なので慎重に使用)。 w ファイル 置換が行われた行を指定のファイルに書き出します。複数行該当した場合は追記します(上書きしません)。 s
コマンドでは、replacement中で特殊シーケンスが使用できます。&
はマッチした全文字列を指し、\\1
,\\2
... は正規表現のキャプチャグループに対応します(()
で囲んだ部分)。例えばs/(foo)/[&]/
は "foo" を "[foo]" に置換します。
[GNU] replacement中では、\\U
や\\L
で以降の英字を大文字/小文字に変換、\\u
や\\l
で次の一文字を変換、といったケース変換シーケンスも使用できます。例: 置換の基本例とフラグ:
$ echo "apple apple" | sed 's/apple/APPLE/' APPLE apple $ echo "apple apple" | sed 's/apple/APPLE/g' APPLE APPLE $ echo "Hello hello" | sed 's/hello/Hi/' Hello Hi $ echo "Hello hello" | sed 's/hello/Hi/i' # [Non-POSIX] 大文字小文字無視でマッチ Hi hello $ echo "Hello hello" | sed 's/hello/Hi/gi' # [Non-POSIX] 両方の "hello" を置換 Hi Hi $ echo "foo bar" | sed -n 's/foo/FOO/p' # 置換が起きた行のみ出力(-nモード) FOO bar $ echo "abc def" | sed -E 's/([a-z]+) ([a-z]+)/\2 \1/' def abc $ echo "like like" | sed 's/like/**&**/' # & はマッチ全体を再利用 **like** like $ echo "abc ABC" | sed -E 's/(abc) (ABC)/\U\1 \L\2/' # [GNU] 大小変換(置換部で大文字小文字変更) ABC abc
1つ目は"apple"という最初の一単語のみが置換され、
g
付きでは両方の"apple"が置換されます。i
フラグにより大文字小文字を無視してマッチさせ、gi
ではテキスト中の全ての "hello"/"Hello" が対象となっています。p
フラグは-n
と組み合わせて、置換が行われた行だけを出力しています。正規表現の例では、([a-z]+) ([a-z]+)
で2つの単語をキャプチャし、入れ替えて出力しています("abc def" → "def abc")。&
はマッチ全体(ここでは "like")の再利用、\\1
などはキャプチャグループの再利用を意味します。最後の例ではGNU sedの拡張で、\\U
が後続の\\1
("abc")を大文字に、\\L
が\\2
("ABC")を小文字に変換しています。 -
a\ text: 追加 (append)。現在の行の直後に指定した text(複数行可)を挿入します。スクリプト中では
a\
の後に改行し、続けて挿入したいテキストを記述します。
例: 3行目の後に "NEW LINE" を差し込む:$ seq 1 4 | sed '3a\ > NEW LINE' 1 2 3 NEW LINE 4
3行目(値 "3" の行)の直後に "NEW LINE" が追加されています。
-
i\ text: 挿入 (insert)。現在の行の直前に text を挿入します(
a
とは逆で前に入る)。
例: ファイルの先頭にヘッダ行を挿入する:$ echo -e "Line1\nLine2" | sed '1i\ > --- HEADER ---' --- HEADER --- Line1 Line2
1行目の前に"--- HEADER ---"という行が挿入されています。
-
c\ text: 変更 (change)。現在の行全体を text で置き換えます。範囲アドレス
addr1,addr2c
とすると、その範囲内の全行を text で置き換え、一度だけ text を出力します(複数行をまとめて一つのテキストに置換するイメージです)。
例: 2行目を別の内容に置換:$ echo -e "a\nb\nc" | sed '2c\ > BETA' a BETA c
元の2行目("b")が"BETA"というテキストに置き換わっています。
-
y/ src / dest /: 文字の一括変換 (transliterate)。srcに含まれる文字を対応するdestの文字に一対一で変換します(
tr
コマンドに似ています)。正規表現ではなく、文字対応表で動作する点がs
コマンドと異なります(パターンマッチではない)。
例: 小文字を対応する大文字に変換:$ echo "Hello 123" | sed 'y/hel/HEL/' HEllo 123
この例では"h"→"H", "e"→"E", "l"→"L"がそれぞれ変換され、"Hello"が"HEllo"になっています(指定していない文字はそのまま)。
-
p(print)コマンド: 既に説明したように、現在行を出力します。通常は
-n
モードとの併用で必要な行のみを出力する用途で使われます。p
コマンド自体は単純なので上記参照。 -
=: 現在の行番号を出力します。行の内容ではなく番号だけを表示したい場合に使用します。
例: 各行番号を付与して出力:$ printf "apple\nbanana\n" | sed '=' 1 apple 2 banana
奇数行に行番号、偶数行に元の内容が出力されています。これはsedが行を処理するごとに、
=
で番号を出力し、その後通常の出力(行内容)をしているためです。行番号と内容を同じ行にしたい場合は工夫が必要です(後述のホールドスペースを利用した方法などがあります)。 -
l: 行を表示しますが、制御文字や非表示文字をエスケープして可視化して出力します。改行は
$
で表示され、タブは\t
のように表示されます。テキストデータの内容をデバッグしたり、空白を強調したい場合に役立ちます。
例: タブや行末の確認:$ printf "foo\tbar\nend " | sed -n 'l' foo\tbar$ end $
1行目に
\t
としてタブが、各行末のスペースは$
で表示され、視覚的に確認できます(2行目は"end "の後にスペースが1つあることを$
の前に空白で示しています)。 -
q: スクリプトの実行を終了(quit)します。オプションで引数を指定すると、その値がsedの終了ステータスになります。
q
は例えば「ある条件を満たしたらそれ以降の処理をやめたい」場合に使います。
例: 10行目を出力したら終了する:$ seq 1 100 | sed -n '10{p; q}' 10
上記では10行目で
p
してからq
で終了するため、1~9行目は出力されず、10行目だけ表示して処理が終わります。 -
r file: 指定したfileの内容を現在行の後に読み込んで出力します(read)。通常はアドレスと組み合わせ、「ある行の後に別ファイルの内容を挿入する」といった用途に使います。
例: テンプレートファイルで、プレースホルダ行の後に別ファイル内容を挿入:# file1.txt の内容: メインコンテンツ <<INSERT HERE>> 終了 # file2.txt の内容: 追加情報1 追加情報2 $ sed '/<<INSERT HERE>>/r file2.txt' file1.txt メインコンテンツ <<INSERT HERE>> 追加情報1 追加情報2 終了
"<<INSERT HERE>>"という行の直後に、file2.txtの内容が挿入されています。
-
w file: 現在行を指定したfileに書き出します(write)。
-n
オプションと組み合わせて抽出結果をファイルに保存したり、特定の行だけを別ファイルに記録する用途に使えます。w
コマンドは実行されるたびにファイルに追記していきます(上書きしない)ので、書き込まれるタイミングに注意が必要です。
例: "ERROR"を含む行だけ別ファイルに抽出:$ printf "ok\nERROR1\nokay\nERROR2" | sed -n '/ERROR/w errors.txt' $ cat errors.txt ERROR1 ERROR2
標準出力には何も出力されませんが(
-n
のため)、errors.txt
ファイルに"ERROR"行のみが保存されています。 -
n: 現在のパターンスペースの内容を出力して破棄し(自動出力モードの場合は重複出力を避けるため通常
-n
と併用します)、直ちに次の入力行を読み込んで処理を続行します。簡単に言えば、「次の行に進む」コマンドです。n
を使うと通常のサイクルをスキップするため、スクリプト内で条件分岐的に次の行を処理したい場合に用います。
例: あるパターンがあった次の行だけ出力:$ printf "a\nTARGET\n1\n2\nTARGET\n3" | sed -n '/TARGET/{n; p}' 1 3
/TARGET/
行にマッチしたらn
で次行に進み、p
でその行を出力しています。"TARGET"行自体は出力されず、その直後の行(上記では "1" と "3")が出力されています。 -
N: 次の行を現在のパターンスペースに追加(改行文字とともに)します。つまりパターンスペースが複数行(現在行+次行の2行分)となります。これにより、
s
コマンドの正規表現で改行をまたぐマッチを処理したり、2行をまとめて扱うことができます。N
を使った後は、パターンスペース内に改行\n
が含まれるため、$
アンカーや.
のマッチ挙動が通常と異なる点に注意が必要です(.
は通常改行にマッチしません)。
例: 2行ずつを1行に結合:$ printf "1\n2\n3\n4" | sed 'N; s/\n/, /' 1, 2 3, 4
上記では一度に2行("1\n2" および "3\n4")がパターンスペースに入り、改行を", "に置換して出力しています。注:
N
を繰り返して全行を結合するには、後述するループ処理(ラベルとb
コマンド)を使う方法や、GNU sedの-z
オプションを使って入力全体を1行扱いにする方法もあります。 -
D: パターンスペースに複数行(改行を含む)が含まれている場合に使われ、パターンスペースの先頭から最初の改行までを削除します。削除後、パターンスペースが空でなければそのまま次のスクリプト命令に移り、空なら通常の次サイクルへ移行します。
D
は主にN
等と組み合わせて特定の区切り(例えば空行や改行)までを繰り返し処理する用途に使われます。
(高度な用途のため詳細は割愛しますが、D
とN
を用いることで指定パターンごとにマルチラインを処理するテクニックがあります。) -
P:
p
の大文字版で、パターンスペースに改行が含まれる場合に最初の改行まで(1行分)だけ出力します。D
とセットで使われることが多く、マルチライン処理で先頭の1行のみ出力し、残りを次サイクルに回すというユースケースに使用します。 -
h / H: ホールドスペースへの保存。
h
は現在のパターンスペースの内容をホールドスペースにコピーし、H
はホールドスペースに追記します。sedにはパターンスペースとは別に一時テキストを保存できるホールドスペースがあり、これを利用して複数行にまたがる処理や値の一時保存が可能です。 -
g / G: ホールドスペースからの取得。
g
はホールドスペースの内容でパターンスペースを置換し、G
はホールドスペースの内容をパターンスペースに追記(現在の内容の後に追加)します。 -
x: パターンスペースとホールドスペースの内容を交換します。
これらのホールドスペース関連コマンドは高度なsedワンライナーで使用されます。例えば「ある行を一時保存し、後の行で利用する」「前後の行を入れ替える」といった操作が可能です。
-
: ラベル、 b ラベル、 t ラベル: これはsedスクリプト内での分岐とジャンプを実現するコマンドです。
:
で任意のラベルを設定し、b
は無条件に指定ラベル(またはラベル省略時はスクリプト末尾)へジャンプします。t
は直前の置換コマンド(s
)が成功した場合のみジャンプします(条件付き分岐)。これらを用いると、sedスクリプト内でループ処理や条件分岐処理が実現できます。高度なテクニックですが、例えばテキスト全体の空行除去や複数行の結合に利用されます。
例: 全文から改行を除去し、一行にまとめる(GNU sedの場合):$ sed ':a; N; $!ba; s/\n/ /g' input.txt
このスクリプトの意味は次の通りです:
:a
はラベルa
の定義。N
は次行をパターンスペースに追加(全文が読み込まれるまで繰り返す)。$!ba
は最後の行でない場合($!
)にb (ジャンプ) a(ラベルa
に戻る)を実行。これで全行が一つのパターンスペースに連結されるまでループします。s/\n/ /g
はパターンスペース中の改行文字を空白に置換(結果として全テキストが1行になる)。
このようにしてファイル全体を一行に集約することができます(
-z
オプションを使う代替方法もあります)。
よくあるユースケースとベストプラクティス
よくあるユースケース:
-
テキストの一括置換: ログや設定ファイル中のパラメータ名を一括で変更する(例:
sed -i.bak 's/OLD/NEW/g' config.txt
)。 -
フィルタとして使用: grepのように特定の行だけ抽出したり(
sed -n '/pattern/p'
)、逆に除去したり(sed '/pattern/d'
)できる。 -
フォーマット変換: 区切り文字の変換(例: カンマをタブに置換)、大文字小文字変換(GNU拡張の
\\L
/\\U
活用)、テキストを囲む(sed 's/.*/「&」/'
で行全体を引用符で囲む)など。 -
行の挿入・付加: ファイルの特定位置にヘッダやフッタを追加したり(例:
sed -i '1i\ヘッダ文字列' file
)、各行末にセミコロンを付け足す(sed 's/$/;/'
)。 -
複数ファイル処理: シェルと組み合わせて、
find
で見つけた複数ファイルに対しsedでインプレース置換を行う(ワンライナーでfor
ループやxargs
と組み合わせ)など。
ベストプラクティス:
-
スクリプトの引用: sedスクリプト中では、正規表現のメタ文字(
$
、*
、\\
、/
など)やシェルで特別な意味を持つ文字($
、\\
、"
など)が登場します。これらがシェルに解釈されないよう、シングルクォート'...'
で囲んでコマンドを書くのが基本です。シングルクォート内ではほとんどの文字がリテラル扱いになるため、安心して書けます(シングルクォート自体はクォート内に記述できないので注意)。 -
正規表現の拡張: 複雑なパターンでは
-E
オプションを使い、拡張正規表現を利用すると表現が簡潔になります。例えば波括弧の量指定子や+
・?
といったメタ文字を直接使えるので、可読性と保守性が向上します。 -
インプレース編集の注意:
-i
オプションで直接ファイルを書き換える際は、事前に出力を確認するかバックアップを取ることを推奨します。特にワンライナーで大量のファイルを一括変換する場合、バックアップ拡張子を付けておけば万一の時に復元可能です。また、環境による-i
の使い方の差異(GNU vs BSD)にも留意し、スクリプトを移植する際は修正が必要か確認しましょう。 -
逐次的なアプローチ: 複雑なテキスト変換は、一度にsedで行おうとせず段階的に処理することも検討してください。sedのスクリプトは一行で多くの処理を詰め込むと理解しづらくなります。必要に応じて
-e
で複数ステップに分けたり、中間結果を確認しつつ進めると安全です。 -
他ツールとの比較: sedは強力ですが、タスクによっては
awk
やperl
、専用コマンド(例:grep
、tr
、cut
)の方が適切・簡潔な場合もあります。例えば「列単位の処理」はawkが得意ですし、「高度な正規表現のパターンマッチ」や「凝った文字列処理」はperlが向いています。適材適所で使い分けましょう。 -
特殊文字の扱い: sedの正規表現ではPCREのようなショートハンド(例えば
\\d
や\\w
)は使えません。代わりにPOSIX文字クラス([[:digit:]]
、[[:alnum:]]
など)を使います。また、改行をパターンに含めたい場合、通常の正規表現の.
ではマッチせず、N
コマンドで取り込むかマルチライン処理用の工夫が要ります(上記参照)。置換の際にバックスラッシュやアンパサンド&
をそのまま出力したい場合、\\
でエスケープする必要がある点にも注意してください。 -
性能: sedは基本的に1行ずつ処理するよう設計され高速ですが、非常に大きなファイルを処理する場合、複雑な正規表現や
N
で全行バッファを扱う操作はメモリ使用量や処理時間に影響します。パターンや範囲指定で必要な行だけ処理することで無駄を減らす、あるいはデータ量が膨大な場合はawkや専用ツールの使用も検討しましょう。
注意すべきポイント・落とし穴
-
シェルによる解釈: sedスクリプト中の
$
や`
(バッククォート)、\\
などはシェルにとって特別な意味を持ちます。これらを正しく扱わないと、意図しないシェル展開やエスケープが起こり、スクリプトが機能しません。シングルクォートでスクリプトを囲むことで大抵の問題は避けられます。また、ワンライナーをシェル内で変数展開した文字列に適用する場合も、適切にエスケープするかクォートすることが重要です。 -
正規表現の非PCRE性: sedはPOSIX基本/拡張正規表現を使います。前読み・後読みなどの高度な正規表現機能(PCREの機能)は使えません。また、
\\w
や\\d
といったショートカットもないため、思ったようにマッチしない場合があります。代わりに[[:alpha:]]
や[[:digit:]]
などの文字クラスを使いましょう。意図せずメタ文字をリテラルとして扱ってしまった場合(例えば+
をエスケープし忘れ)もマッチ漏れの原因になります。複雑な条件は場合によってはawkやgrep -P
(PCRE対応版)の使用を検討してください。 -
インプレース編集の罠:
sed ... > file
のように同じファイルにリダイレクトして出力するのは、内容を上書きしてしまうため誤った方法です。これはファイルを空にしてからsedを実行することになり、意図した結果が得られません。ファイルに対する変更は、-i
オプションで安全に行うか、別の一時ファイルに出力してから置き換える方法を取りましょう。-i
使用時でもバックアップなしで実行すると取り返しがつかないため、初めからバックアップを取っておくか、テストを十分行ってから実行することを推奨します。 -
環境依存: sedの実装にはGNU系とBSD系などの違いがあり、一部オプションや挙動が異なります。特に
-i
オプションの引数の扱い(前述)、拡張正規表現の指定方法(GNUでは-r
も可、BSDでは-E
のみ)などに違いがあります。スクリプトを他の環境に持って行く際には、こうした違いを踏まえて動作確認を行ってください。 -
意図しない複数回の出力: sedを使い始めた人が陥りがちな落とし穴として、
p
コマンドによる出力重複があります。例えばsed 'p' file
とすると、自動出力とp
による出力で各行が二重に表示されます。特定の行だけ表示したい場合は-n
オプションを併用し、その行でのみp
する、といった書き方をする必要があります(上記参照)。 -
特殊文字やデリミタのエスケープ: 置換パターンや挿入テキスト中にスラッシュ
/
を書くと、コマンドの区切りと紛れるためエスケープ\/
が必要です。sedでは置換コマンドのデリミタを他の文字(例えばsed 's#foo#bar#g'
のように#
を区切りに使う)に変更できますので、エスケープが煩雑になる場合はデリミタを変えるのも一法です。同様に、置換結果に&
や\\
を含めたい場合はそれぞれ\\&
、\\\\
と書かなければなりません(そうしないと&
はマッチ全体、\\
はエスケープシーケンスと解釈されます)。 -
最終行の改行: sedは入力の各行末の改行を内部的には外して処理し、出力時に改めて改行を付けます。ただし、入力ファイルの最後の行に改行がない場合、sedの出力にはその最後の改行が付加されません(POSIX準拠の動作です)。そのため、パイプラインで後続のコマンドに渡す際に末尾の行が前のコマンドと繋がってしまう現象が起こりえます。必要なら、出力後に明示的に改行を追加する処理を検討してください。
-
巨大なテキストの処理: sedはストリーム処理に優れますが、
N
やホールドスペースで大量のテキストを一度に扱うとメモリを多く消費します。例えば全ファイルを一行に連結するような処理はメモリに収まらない可能性もあります。巨大ファイルでは一行ずつの処理を原則とし、どうしても全体集約が必要な場合はシステムリソースに注意しましょう。
練習問題
- 文字列の置換: 次の文字列
"apple banana apple"
から、単語"apple"
をすべて"APPLE"
に置換して出力するsed
コマンドを書いてください。 - 空行の削除: テキストファイルから空行(全く文字のない行)を削除する
sed
コマンドを示してください。 - 特定範囲の行の表示: ファイル
example.txt
の10行目から20行目までをsed
で表示するコマンドを書いてください(他の行は表示しないものとします)。 - フィールドの並べ替え:
"Doe, John"
のように「苗字, 名前」の形式で書かれた文字列を「名前 苗字」の形式に変換するsed
のコマンドを書いてください。 - インプレース編集: ファイル
data.txt
中の文字列"YES"
を"NO"
に全て置換し、結果を同じファイルに保存するコマンドをsed
を使って実現してください(バックアップファイルは不要)。
解答
-
文字列中の全ての
"apple"
を"APPLE"
に置換するには、s
コマンドにg
フラグを付けます。echoで文字列を流し込んでテストできます。$ echo "apple banana apple" | sed 's/apple/APPLE/g' APPLE banana APPLE
"apple"
という語が2箇所とも大文字化されていることが確認できます。 -
空行は正規表現で
^$
(行頭から行末まで何もない)と表現できます。それにマッチした行をd
で削除します。sed '/^$/d' file.txt
これにより
file.txt
から空行が取り除かれ、他の行だけが出力されます。-i
を付ければファイルを直接編集できます(例:sed -i '/^$/d' file.txt
)。なお、タブや空白だけの行も削除したい場合は正規表現を/^[[:space:]]*$/d
のようにすると空白類のみの行も削除対象になります。 -
特定の行番号範囲(10~20行目)を表示するには、その範囲をアドレス指定して
p
コマンドで出力し、他は出力しないよう-n
オプションを使います。sed -n '10,20p' example.txt
これは
example.txt
の10行目から20行目までを順に表示します。 -
"Doe, John"
→"John Doe"
の変換には、正規表現のキャプチャを活用します。,
で区切られた2つの部分を入れ替えるには、例えば次のようにします(-E
オプションで拡張正規表現を使用)。$ echo "Doe, John" | sed -E 's/^([^,]+),[[:space:]]*(.*)$/\2 \1/' John Doe
正規表現の解釈:
^([^,]+),[[:space:]]*(.*)$
は最初にカンマ以外の文字を1つ以上([^,]+)
(苗字)としてキャプチャし、続く,
と任意の空白([[:space:]]*
)、残りを(.*)
(名前)としてキャプチャします。置換ではそれらを逆転して\\2 \\1
とすることで「名前 苗字」の順に出力しています。 -
sed
のインプレース編集でファイル内の"YES"
を"NO"
に置換します。GNU sed環境では以下のようになります(macOSの場合は-i ''
と拡張子の空指定が必要な点に注意)。sed -i 's/YES/NO/g' data.txt
これは
data.txt
内のすべての"YES"を"NO"に置換し、結果を同じファイルに保存します(バックアップ無し)。実行前に期待通りの置換になるか、バックアップを取るか、テスト環境で試すことをお勧めします。