【 sed 】コマンド(応用編その2)――ホールドスペースの活用Linux基本コマンドTips(213)

本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回は、テキストファイルをフィルター処理で編集する「sed」コマンドです。

» 2018年06月07日 05時00分 公開
[西村めぐみ@IT]
「Linux基本コマンドTips」のインデックス

Linux基本コマンドTips一覧

 本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。テキストファイルをフィルター処理で編集する「sed」コマンドです。

 連載第53回第54回第55回第56回第57回に続き、sedの応用を説明します。

sedコマンドとは?

 「sed」は「Stream EDitor」の略です。「sed スクリプトコマンド ファイル名」で、指定したファイルをスクリプトコマンドに従って行単位に処理し、標準出力へ出力します。ファイル名を省略した場合は、標準入力からのデータを処理します。

 sedコマンドを利用する場合、パイプとリダイレクトを活用することが一般的です。



sedコマンドの書式

sed [オプション]

sed [オプション] スクリプトコマンド 入力ファイル

※[ ]は省略可能な引数を示しています





sedの主なオプション

短いオプション 長いオプション 意味
-r --regexp-extended スクリプトで拡張正規表現を使用する
-e スクリプト --expression=スクリプト スクリプト(コマンド)を追加する
-f スクリプトファイル --file=スクリプトファイル 実行するコマンドとしてスクリプトファイルの内容を追加する
-i --in-place ファイルを直接編集する
-i拡張子 --in-place=拡張子 ファイルを直接編集し、指定した拡張子でバックアップする(「-i」と「拡張子」の間には空白を入れない)
--follow-symlinks -iで処理する際にシンボリックリンクをたどる
-n --quiet,--silent 出力コマンド以外の出力を行わない(デフォルトでは処理しなかった行はそのまま出力される)
-l 文字数 --line-length=文字数 lコマンドの出力行を折り返す長さを指定する(「-l」と「文字数」の間には空白を入れる)
-s --separate 複数の入力ファイルを一続きのストリームとして扱わずに個別のファイルとして扱う
-u --unbuffered 入力ファイルからデータをごく少量ずつ取り込み、頻繁に出力バッファを掃き出す
-z --null-data NUL文字で行を分割する(通常は改行で分割)
--posix 全てのGNU拡張を無効にする

sedの主なコマンド

 sedでは、「アドレス」と「コマンド」の組み合わせで処理を指定します。

 アドレスには行番号や正規表現による指定が可能で、省略した場合は全ての行が処理の対象となります。

コマンド 意味
= 現在の行番号を出力する
a テキスト テキストの追加。指定した位置の後ろに[テキスト]を挿入する(挿入するテキストに改行を含める場合は、改行の前にバックスラッシュを置く)
i テキスト テキストの挿入。指定した位置の後ろに[テキスト]を挿入する(挿入するテキストに改行を含める場合は、改行の前にバックスラッシュを置く)
c テキスト 選択した行を[テキスト]で置換する(挿入するテキストに改行を含める場合は、改行の前にバックスラッシュを置く)
q これ以上入力を処理せずに終了する(未出力分があれば、出力してから終了する)
Q これ以上入力も出力もせずに終了する
d 指定した行を削除する
p 処理した内容を出力する(「-n」オプション指定時は「p」コマンドがないと何も出力されなくなる)
s/置換前/置換後/ [置換前]で指定した文字列にマッチした部分を[置換後]に置き換える。複数マッチした場合は先頭のみ置換、全てを置換したい場合は、「s/置換前/置換後/g」のように「g」オプションを指定する
y/元の文字列/対象文字列/ [元の文字列]にあるものを、対象文字列の同じ位置にある文字に置換する(「tr」コマンドのように使用できる)
# コメント(スクリプト中、「#」以降がコメントとなる)


パターンスペースとホールドスペースを使う

 sedコマンドは、「テキストを1行読み込み、sコマンドなどで編集操作を行い、出力する」という処理を行ごとに繰り返すことで、ファイルを編集します。一連の操作を行うsedコマンド内部の場所を「パターンスペース」と呼びます。

 sedは、この「パターンスペース」とは別に、処理中のデータを一時的に保存しておく場所を備えています。これを「ホールドスペース」と呼びます。

 パターンスペースとホールドスペースは独立していますが、sedのコマンドによって内容をやりとりできます。

 例えば「h」コマンドで、現在のパターンスペースの内容を、ホールドスペースにコピーします。ただし、ホールドスペースにデータがあった場合は上書きします。上書きではなく、追加したい場合は「H」コマンドを使用します。

 以下にホールドスペースとパターンスペースについて利用できるsedのコマンドを示しました。コマンドの処理は全て行単位です。

ホールドスペースを操作するコマンド

コマンド 意味
h 現在のパターンスペースの内容を、ホールドスペースに上書きコピーする
H 現在のパターンスペースの内容を、ホールドスペースに追加する
g ホールドスペースに保存した内容を、パターンスペースに上書きコピーする
G ホールドスペースに保存した内容を、パターンスペースに追加する
x パターンスペースの内容とホールドスペースの内容を交換する


ホールドスペースを使った処理(1)

 ホールドスペースを使った処理を試してみましょう。処理内容は、/etc/shellsの内容のうち、末尾がshとなっている行のみ、「/bin/bash -> /bin/baSH」のよう置き換えるというものです(※1)。

※1 ホールドスペースを使わず、sコマンドでも処理できる。「sed -ne 's/\(.*\)sh$/& -> \1SH/p'」と書けばよい。\(〜\)」は文字列の一部を取り出すために使用した。「&」は置換対象となった箇所全体という意味だ(第57回)。



 末尾がshとなっている行を探し出したいため、パターン指定は「/sh$/」としました。さらに、複数の処理を行いたいので、「{ }」記号でコマンドをグループ化しています。

 以下のスクリプトでは読みやすくするためインデントしました。#記号以降はコメントです。

/sh$/{		#末尾がshだった場合 { 〜 } の処理を行う
  h		#読み込んだ内容をホールドスペースに保存(上書きコピー)
  s/$/ -> /	#末尾に「 -> 」を追加する
  x		#パターンスペースとホールドスペースの内容を交換する
  s/sh/SH/	#shをSHに置換する(※2)
  H		#パターンスペースの内容をホールドスペースに追加する
  x		#パターンスペースとホールドスペースの内容を交換する
  s/\n//	#改行を除去する
  p		#出力する
}

※2 このスクリプトを、/etc/shells以外に適用すると、意図した通りの結果にはならない。例えば、「shshsh」という行が、「SHshsh」に置換されてしまう。これを避けるためには、「行末のshをSHに置換」となるように厳密に指定しなければならない。具体的には「s/sh$/SH/」のように指定する。



 「sedは1行ずつ順次処理するエディタだ」ということを意識するとスクリプトを組立てやすいでしょう。「置換や出力の処理はパターンスペースに対して行う」という点に注意してください。ホールドスペースに対しては置換や出力の処理ができません。

 各コマンドを実行した後のパターンスペースの内容とホールドスペースの内容は以下のようになります。ここでは「/bin/bash」という行を読んだ時を想定しています。

スプリプト実行中の内部の状態

コマンド 処理の意味 パターンスペースの内容 ホールドスペースの内容
(1行読み込む) /bin/bash (※3)
h ホールドスペースにコピー /bin/bash /bin/bash
s 置換 /bin/bash -> /bin/bash
x 交換 /bin/bash /bin/bash ->
s 置換 /bin/baSH /bin/bash ->
H ホールドスペースに追加 /bin/baSH /bin/bash -> 改行/bin/baSh
x 交換 /bin/bash -> 改行/bin/baSH /bin/baSH
s 置換 /bin/bash -> /bin/baSH /bin/baSH
p 出力 /bin/bash -> /bin/baSH /bin/baSH

※3 最初は空だが、それ以降は直前に保存した内容となる。



 スクリプトを1行で短く書くことができるので、画面1ではコマンドラインに直接入力しました。コマンドとコマンドの区切りには「;」記号を使用しています。なお、スクリプトファイルで処理する場合は、「-e」オプションを使わず「-n」オプションと「-f」オプションを使用してください(第54回)。

コマンド実行例

cat /etc/shells | sed -ne '/sh$/{h;s/$/ -> /;x;s/sh/SH/;H;x;s/\n//;p}'

(/etc/shellsの内容を読み込み、行末の「sh」を「SH」に置き換える)


画面1 画面1 /etc/shellsの内容の一部を置換したところ


ホールドスペースを使った処理(2)

 先ほど示したsedのスクリプトには改善の余地があります。処理の順番を変えることで、「x」コマンド(パターンスペースとホールドスペースの交換)を1回減らすことができます。

 sedはあくまでも1行単位で処理していることと、HコマンドとGコマンドは、常に文字列の後に追加していること(リダイレクト記号の「>>」相当)に注意してください。

 画面2ではスクリプトファイルを読み込まず、コマンドラインで直接入力しています。

/sh$/{		#末尾がshだった場合 { 〜 } の処理を行う
  h		#読み込んだ内容をホールドスペースに保存(上書きコピー)
  s/sh/SH/	#shをSHに置換する(※2)
  x		#パターンスペースとホールドスペースの内容を交換する
  s/$/ -> /	#末尾に「 -> 」を追加する
  G		#ホールドスペースの内容をパターンスペースに追加する
  s/\n//	#改行を除去する
  p		#出力する
}

コマンド実行例

cat /etc/shells | sed -ne '/sh$/{h;s/sh/SH/;x;s/$/ -> /;G;s/\n//;p}'

(/etc/shellsの内容を読み込み、行末の「sh」を「SH」に置き換える)


画面2 画面2 /etc/shellsの内容の一部を置換したところ


筆者紹介

西村 めぐみ(にしむら めぐみ)

PC-9801NからのDOSユーザー。PC-486DX時代にDOS版UNIX-like toolsを経てLinuxへ。1992年より生産管理のパッケージソフトウェアの開発およびサポート業務を担当。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『Accessではじめるデータベース超入門[改訂2版]』『macOSコマンド入門』など。2011年より、地方自治体の在宅就業支援事業にてPC基礎およびMicrosoft Office関連の教材作成およびeラーニング指導を担当。


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。