今回も、前回に引き続きsedコマンドでよく使われる置換機能「s」について説明する。コマンドについては前回同様、「BSD sed」と「GNU sed」の両方の実行結果を示す。「sed」がBSD sedのコマンドで、「gsed」がGNU sedだ。
範囲指定と出力「p」
sedコマンドでは置換機能がよく使われるが、範囲を指定して抜き出すといった処理も結構便利だ。行単位でデータを抜き出すような用途であれば、grepコマンドが最強クラスなのだが、「ある行からある行まで」といった指定を行う場合はsedコマンドを使うことになる。
例えば、次のようなテキストファイルを用意したとする。
% cat man.txt
NAME
sed - stream editor
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
DESCRIPTION
The sed utility reads the specified files, or the standard
input if no files are specified, modifying the input as
specified by a list of commands. The input is then written
to the standard output.
%
sedコマンドで範囲指定を行う方法で、最もわかりやすいのは行数を指定する方法だろう。まずは、catコマンドで行番号を確認しておこう。
% cat -n man.txt
1 NAME
2 sed - stream editor
3
4 SYNOPSIS
5 sed [-Ealnru] command [file ...]
6 sed [-Ealnr] [-e command] [-f command_file] [-I extension]
7 [-i extension] [file ...]
8
9 DESCRIPTION
10 The sed utility reads the specified files, or the standard
11 input if no files are specified, modifying the input as
12 specified by a list of commands. The input is then written
13 to the standard output.
%
このテキストファイルに対し、sedコマンドに「-n 4,7p」と指定した場合、「4行目から7行目までを出力し、それ以外の行は出力しない」という処理が行われる。
% sed -n 4,7p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
% gsed -n 4,7p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
%
「4,7p」の指定が「4行目から7行目までを出力する」という意味で、「-n」が「それ以外の行は出力しない」という意味だ。
この範囲指定を使って「○行目に加えて、さらに○行分」という指定もできる。記述形式は、次のようになる。
% sed -n 4,+3p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
% gsed -n 4,+3p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
%
「4,+3p」という指定が、「4行目に加えてその次の行から3行分を出力」ということを意味している。
この範囲指定が強力なのは、行数指定ではなく正規表現を利用できる点にある。次のように最初の行数を指定している部分を「/^SYNOPSIS\$/」とすると、「この正規表現に一致した行から(処理を始める)」という指定になる。
% sed -n /^SYNOPSIS\$/,+3p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
% gsed -n /^SYNOPSIS\$/,+3p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
%
スラッシュで囲った文字列が正規表現であり、この場合「^SYNOPSIS\$」が該当する。指定中のバックスラッシュは、シェルが変数展開の指定として解釈しないようにするためのものなので、正規表現部分は「^SYNOPSIS$」だけだ。「^」が行頭、「$」が行末を意味しているため、「^SYNOPSIS$」というのは「『SYNOPSIS』という文字列だけから成る行」を表している。
出力の範囲として、出力を終える部分でも正規表現を指定できる。例えば、次のような感じだ。
% sed -n /^SYNOPSIS\$/,/^\$/p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
% gsed -n /^SYNOPSIS\$/,/^\$/p man.txt
SYNOPSIS
sed [-Ealnru] command [file ...]
sed [-Ealnr] [-e command] [-f command_file] [-I extension]
[-i extension] [file ...]
%
「/^SYNOPSIS\$/,/^\$/」は、シェルの変数展開を避ける処理を除けば「/^SYNOPSIS$/,/^$/」という指定だ。つまり、「『SYNOPSIS』という文字列だけの行から、空行までを出力する」という指定になる。このように正規表現で範囲を指定できると、何行目から処理を行いたいのかがわからない場合でも、特定の範囲を抜き出すことが可能になる。
UNIXのコマンドは行単位のデータ処理を得意とするものが多く、このように「行をまたいで範囲を指定してデータを取り出す」といった処理ができるコマンドはほとんど存在しない。その点、範囲指定ができるsedコマンドは便利であり、実際、そうした目的でもよく使われている。ぜひとも覚えておいてほしい使い方だ。