文字列を全置換したり、行単位で抽出したり、削除したり、いろいろなテキスト処理のできるコマンド。処理内容はコマンドラインパラメータで指定して、非対話的に一括処理できる。
sedで書ける処理であれば、処理内容にもよるが、perlのワンライナーで書くよりも速いことが多い。
sedというコマンド名は Stream EDitor の略らしい。
標準入力をなにかしら処理して標準出力に出力するので、パイプとして使える。まあたいていのUNIXツールはパイプとして使えるのだが。
ファイル名を指定すればそのファイルを読み込んで処理して標準出力に出力する。
ファイル名を指定してオプション-i
を使えば、そのファイルを読み込んで、結果をそのファイルに上書きする。ファイル自体を編集したい場合には便利。(-i
はGNU sed限定)
処理内容は、置換処理や行の削除、選択などいろいろできて、オプション-e
で指定する。
パイプで使う場合の例
$ cat ./hoge.txt | sed -e 's/xxx/XXX/g' > ./hoge-new.txt
ファイル名を指定して使う場合の例
$ sed -e 's/xxx/XXX/g' ./hoge.txt > ./hoge-new.txt
ファイルを書き換えてしまう例(複数のファイルを指定できる)
$ sed -i -e 's/xxx/XXX/g' ./hoge.txt ./hoge2.txt ./hoge3.txt
-e
オプションがない場合はオプション以外の最初の引数が処理内容とみなされるので、たいていは-e
を省略できる。処理内容を指示する引数に拡張正規表現を使いたい場合は、後述のように-E
または-r
を付ける。
環境によっては-E
や-r
がサポートされていないみたい。
-e
オプションを複数指定すれば、コマンドを複数実行することができる。またはコマンドを;
で区切って複数並べることもできる。
コマンドが複数ある場合、コマンド1つずつ全行操作するのではなく、各行ごとにコマンドをすべて実行していく。つまり、以下の2つの方法は結果が違う可能性がある。
$ cat ... | sed -e ... | sed -e ...$ cat ... | sed -e ... -e ...
例えば、1つ目のコマンドに行番号を指定して行を削除する処理があり、2つ目のコマンドに行番号を指定して出力する処理がある場合、上記2つの方法のいずれかによって行番号がずれる可能性がある。
-e
で指定する処理内容2016/09/27-e
のあとのパラメータは、アドレス、コマンド1文字、コマンドパラメータの順に書くことで、処理内容を表す。アドレスはどの行を処理対象とするかを表す。すべての行を処理する場合はアドレスを省略できるので、コマンドの1文字から始まることになる。コマンドはs
,d
,p
などがある。
例
$ sed -e 's/xxx/XXX/g' # sコマンド (アドレスがなくて、sのあとはパラメータ)$ sed -e '30,31d' # dコマンド (dの前にアドレスがあり、パラメータがない)$ sed -n -e '30p' # pコマンド (これもアドレスがあり、パラメータがない)
s
コマンド2015/02/01s
コマンドは正規表現で置換処理をする。
$ sed -e 's/abc/ABC/g'
最後のg
はすべてのマッチした文字列を置換することを意味する。g
がなくても全行で置換を実行するが、1行に2つ以上マッチした場合は1つ目しか置換されない。
g
がなくても全行の置換をするが、1行に複数マッチする場合でも各行の最初のマッチしか置換をしない。
区切り記号の/
は他の記号でもよく、パスの置換などで置換対象に/
が含まれている場合は、!
など他の記号を使ったほうが便利。
上の例ではabc
が置換前の正規表現、ABC
が置換後の文字列。
置換後の文字列に&
を指定するとマッチした文字列の部分が出力され、,
... を指定するとマッチした文字列のうち正規表現内でカッコでグルーピングされた部分が出力される。
$ echo xx34 | sed -e 's/xx\(.\)/-& -/g'## => -xx3 3-4$echo xx34 | sed -E 's/xx(.)/-& -/g'## => -xx3 3-4
-e
と-E
の違いは基本正規表現と拡張正規表現を参照。
-n
オプションを追加してp
フラグを付けると、マッチした行のみを出力する。
s
コマンドの前に数字を置くと、その行のみがマッチングの対象になる。(アドレスという)
## 3行目のみを全置換の対象とする## gを付けているのでその行に複数マッチすれば全部置換される$ sed 3s/abc/ABC/g## 3行目から5行目のみを全置換の対象とする$ sed 3,5s/abc/ABC/g## 3行目から5行目のみを全置換の対象とする$ sed '3,$s/abc/ABC/g'
d
コマンド2015/05/13d
コマンドは行を削除する。
sed d
とすると、すべての行が削除されてなにも出力しなくなってしまう。普通は後述のアドレスの機能を使って削除する行を指定して、残りを出力させる。
## 1行目から5行目を削除して6行目以降を出力$ sed 1,5d
!
を付けると、逆に対象行以外を削除するコマンドになる。
## 1行目から行目のみを出力## ! はシェルが特別に解釈してしまうので、## シングルクオーテーションを付けるか \ でエスケープが必要$ sed '1,5!d'$ sed 1,5\!d
p
コマンド2013/10/01行を単に出力する。通常は-n
オプションと組み合わせる。sed
コマンドでは処理結果をデフォルトで出力するが、-n
オプションを付けるとデフォルトの出力がされなくなり、p
コマンドの出力のみになる。
コマンドの実行対象行を指定する行番号をアドレスという。行番号だけでなくや正規表現で指定して、その正規表現にマッチする行を実行対象とすることもできる。
アドレスは-e
のあとの処理内容を表すコマンドの前に付ける。
以下の例はp
コマンドでの例だが、s
,y
,d
コマンドなどでも使える。
# 先頭の行のみを出力sed -n -e 1p# 最後の行のみを出力# \ はシェルのエスケープsed -n -e \$p# 6行目から15行目を出力sed -n -e 6,15p# 奇数行のみを出力sed -n -e 1~2p# 1行目、6行目、11行目、16行目、、、を出力sed -n -e 1~5p# 正規表現にマッチする行を出力sed -n -e /xxx/p# これは以下と同じgrep -e xxx# 逆に正規表現にマッチしない行を出力(dコマンドでの例)sed -e /xxx/d# これは以下と同じgrep -v -e xxx# 正規表現にマッチする行を出力# 先頭に \ を付ければ、正規表現を囲む記号はなんでもよいsed -n -e '\%xxx%p'# 1つ目の正規表現にマッチする行から2つ目の正規表現にマッチする行までを出力sed -n -e /xxx/,/yyy/p# 正規表現にマッチする行から3行分を出力# 正規表現にマッチするごとに最低3行が出力されるsed -n -e /xxx/,+3p# 正規表現にマッチする行から最後までを出力# $ が最後という意味になるsed -n -e '/xxx/,$p'# 20行目から23行目までの4行分を出力sed -n -e 20,+3p
s
コマンドでアドレスの機能を使うと、該当する行だけ置換処理が実行され、それ以外の行は置換せずにそのまま出力される。
-e
script-E
-i
,--in-place
-n
-p
コマンドによる出力のみになる。-r
-E
と同じ。-u
tail -f
と組み合わせるときに有用。--help
--version
sedで使える正規表現は基本正規表現と拡張正規表現(extended regular expressions)の2種類ある。
-E
または-r
を付けると拡張正規表現になり、それ以外は基本正規表現になる。
基本正規表現と拡張正規表現とで扱いが異なるのは以下の7文字のみである。
+ ? { } ( ) |
これらの文字の前にバックスラッシュでエスケープするかどうかで、正規表現での特殊な意味になるか、単にその文字そのものの意味になるかが、基本正規表現と拡張正規表現とで変わる。拡張正規表現という名前の割には機能が上がっているわけではない。
以下は自分の環境(GNU sed 4.2.2)で検証した結果。
まずは+
の例。拡張正規表現では+
で直前の文字が1文字以上の意味になり、 で
+
そのものを表すが、基本正規表現では逆になる(?)。(その説明だと2番目の実行例が説明できない)
$ echo '+' | sed 's/+/OK/g'OK$ echo '+' | sed 's//OK/g'OK$ echo '+' | sed -E 's/+/OK/g'sed: -e expression #1, char 8: Invalid preceding regular expression$ echo '+' | sed -E 's//OK/g'OK$ echo 'x' | sed 's/x+/OK/g'x$ echo 'x' | sed 's/x/OK/g'OK$ echo 'x' | sed -E 's/x+/OK/g'OK$ echo 'x' | sed -E 's/x/OK/g'x
次は?
の例だが、これは+
と同様。拡張正規表現では?
で直前の文字が0文字または1文字の意味になり、?+
で?
そのものを表すが、基本正規表現では逆になる(?)。(やはり2番目の実行例が説明できない)
$ echo '?' | sed 's/?/OK/g'OK$ echo '?' | sed 's/\?/OK/g'OK$ echo '?' | sed -E 's/?/OK/g'sed: -e expression #1, char 8: Invalid preceding regular expression$ echo '?' | sed -E 's/\?/OK/g'OK$ echo 'x' | sed 's/x?/OK/g'x$ echo 'x' | sed 's/x\?/OK/g'OK$ echo 'x' | sed -E 's/x?/OK/g'OK$ echo 'x' | sed -E 's/x\?/OK/g'x
続いて{}
の例。{}
は正規表現でとしては以下のような意味がある。
{
n}
{
n,}
{
n,m}
+
や?
と同様に拡張正規表現ではそのまま使えるが、基本正規表現ではバックスラッシュを付けないといけない。
$ echo '{' | sed 's/{/OK/g'OK$ echo '{' | sed 's/\{/OK/g'sed: -e expression #1, char 9: Invalid preceding regular expression$ echo '{' | sed -E 's/{/OK/g'sed: -e expression #1, char 8: Invalid preceding regular expression$ echo '{' | sed -E 's/\{/OK/g'OK$ echo 'xx' | sed 's/x{2}/OK/g'xx$ echo 'xx' | sed 's/x\{2\}/OK/g'OK$ echo 'xx' | sed -E 's/x{2}/OK/g'OK$ echo 'xx' | sed -E 's/x\{2\}/OK/g'xx
()
や|
も同様に拡張正規表現ではそのまま使えるが、基本正規表現ではバックスラッシュを付けないといけない。逆にその文字そのものは、基本正規表現ではそのまま書けばよいが、拡張正規表現ではバックスラッシュが必要。
$ echo '(' | sed 's/(/OK/g'OK$ echo '(' | sed 's/\(/OK/g'sed: -e expression #1, char 9: Unmatched ( or \($ echo '(' | sed -E 's/(/OK/g'sed: -e expression #1, char 8: Unmatched ( or \($ echo '(' | sed -E 's/\(/OK/g'OK$ echo 'a|b' | sed 's/a|b/OK/g'OK$ echo 'a|b' | sed 's/a\|b/OK/g'OK|OK$ echo 'a|b' | sed -E 's/a|b/OK/g'OK|OK$ echo 'a|b' | sed -E 's/a\|b/OK/g'OK
全部大文字にするには\U
というのを使うとよい。U
はたぶんUpper の略。
head main.go | sed -E 's/(.*)/\U/'# またはhead main.go | sed 's/\(.*\)/\U/'
小文字にするには\L
というのを使うとよい。L
はたぶんLower の略。
head main.go | sed -E 's/(.*)/\L/'# またはhead main.go | sed 's/\(.*\)/\L/'
{}
を使うには2015/12/19正規表現で文字数を指定する{}
は上で説明したとおり\
でエスケープするか、オプション-r
を付けるかまたは-e
の代わりに-E
を使う必要がある。
# xxxx を XXXX に置換する例sed 's/x{4}/XXXX/g' # => NGsed 's/x\{4\}/XXXX/g' # => OKsed "s/x{4}/XXXX/g" # => NGsed "s/x\{4\}/XXXX/g" # => OKsed "s/x\\{4\\}/XXXX/g" # => NG
()
で囲まれた部分を使って置換するには2015/02/01置換後の文字列には、,
などを指定することで、正規表現にマッチしたグループに置き換えられる。
echo "bc abcc" | sed 's/b\(c*\)//g'# => c accecho "bc abcc" | sed -e 's/b\(c*\)//g'# => c accecho "bc abcc" | sed -E 's/b(c*)//g'# => c acc
正規表現の中の()
には\
でエスケープしないといけない。-e
の代わりに-E
をつけると、上で説明したとおり拡張正規表現になってエスケープが不要になる。
## タブ文字をスペースに全置換$ sed '/\t/ /g'
# '2013' が含まれる行を削除sed '/^2013/d'# 空行を削除sed '/^$/d'
# '2013' が含まれる行のみを出力sed '/^2013/!d'# またはsed -n '/^2013/p'# 空行の数をカウントsed -n '/^$/p' | wc -l
# タブ文字をスペースに全置換sed -E 's/[\t ]+/ /g'
sedでなくてcatコマンドの-s
オプションで簡単にできる。
$ cat -s input.txt
GNU sedであれば、-i
オプションを付ければ、ファイルを直接書き換えることができる。
sed -i -e 's/置換前/置換後/g' ファイル名
-i
がないと、全置換後の結果を標準出力に吐き出して、ファイルは書き換えない。
ちなみに以下のようにしてもうまくいかない。
cat foo.txt | sed -i -e 's/.../.../g' > foo.txt
ファイルからの入力と結果の書き出しは並列で実行されるため、標準入力から読み込もうにも結果を書き出すために先にファイルサイズが0になってしまい、結果としてfoo.txt
はからのファイルになる。
BSDやMacに入っているsedはGNU sedではなく、-i
オプションがないらしい。
-e
で2つ以上の全置換を実行できる。
cat ファイル名 | sed -e 's/置換前/置換後/g' -e 's/別の置換前/別の置換後/g'
プログラミング言語でいう部分配列とかスライスみたいな。
## 2行目から4行目までを抽出(4行目を含む)$ sed -n 2,4p foo.txt## 2行目のみを抽出$ sed -n 2p foo.txt## 2行目から最後までを抽出$ sed -n '2,$p' foo.txt## または$ sed -n 2,\$p foo.txt## 先頭から4行目までを抽出$ sed -n 1,4p foo.txt## または$ sed 4q foo.txt
q
はその行で終了という意味。
# 2行目から4行目を削除(4行目も削除)cat ファイル名 | sed -e 2,4d# 2行目のみを削除cat ファイル名 | sed -e 2d# 2行目から最後まで削除cat ファイル名 | sed -e '2,$d'# またはcat ファイル名 | sed -e 2,\$d# または head でも同じことができるcat ファイル名 | head -n 1# 最後の行のみを削除cat ファイル名 | sed -e \$d
## 1行目、11行目、21行目、、、と10行おきに抽出$ cat ファイル名 | sed -ne '1~10p'## 10行目、20行目、、、と10行おきに抽出$ cat ファイル名 | sed -ne '0~10p'
perlのワンライナーで以下のようにも書けるが、単純にN行おきに抽出するだけのシンプルな処理であればsedのほうが速い。
$ cat ファイル名 | perl -nle '$.%10==0&&print'
s/.../..../g
の記法のスラッシュは記号なら比較的なんでもよくて、3つ同じ記号を使っていることに意味があるので、s@...@...@g
のようにも書ける。
例
cat ファイル名 | sed -e 's@/etc/foo/bar@/home/my/etc/foo/bar@g'
tail -f
の出力をsedで処理しながら表示するには2013/07/09tail -f access_log | sed --unbuffered ...# またはtail -f access_log | sed -u ...
--unbuffered
または-u
を付けないとsedがバッファリングをしてしまって、リアルタイムに表示されなくなってしまう。
^$
という正規表現と、d
での削除コマンドを使う。
cat ファイル名 | sed '/^$/d'
空白文字だけの行も空行とみなして削除するには、
cat ファイル名 | sed '/^[[:blank:]]*$/d'
または
cat ファイル名 | sed '/^\s*$/d'
(違いは調べていない、、、)
1行目がヘッダ行などでこれは正規表現に関係なく残しておいて、残りの行は正規表現にマッチしたもののみにしたい場合、
1行目を抽出するコマンド1p
と正規表現で抽出するコマンドを組み合わせる。
cat ファイル名 | sed -n -e 1p -e /パターン/p
sed 3i test
のように書くと、3行目にtest
という行を挿入する、という意味になる。
また、sed 3a test
と書くと、3行目の次にtest
という行を挿入する、という意味になる。
$ seq 512345$ seq 5 | sed '3i test'12test345$ seq 5 | sed '3a test test'123test test45