Mac のターミナルで sed コマンドを利用した際、期待通りに動かないことがあったので調べてみたメモ。
sed コマンドとは
sed(Stream EDitor)は、入力として受け取ったテキストに対して、変換など処理を行うコマンド。 端的にいうと文字列の加工ができるコマンドである。
例えば、マッチした文字列の一部を置換したい時などに使う。以下の例では、-e の後に置換するルールを記述しており、World
を Japan
に置換している。
% echo "Hello, World\!" | sed -e "s/World/Japan/g" Hello, Japan!
パイプで入力を受け取る以外には、ファイルを入力として受け取り処理を行うこともできる(処理結果は標準出力されるのみのため、ファイルに出力したい場合は後述の -i オプションを利用する)。
% echo "Hello, World\!" > hello.txt % sed -e s/World/Japan/g hello.txt Hello, Japan!
Mac で使える sed について
% which sed /usr/bin/sed % sed --version sed: illegal option -- - usage: sed script [-Ealn] [-i extension] [file ...] sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]
ただし、Mac で標準になっている sed は BSD 系のもの(POSIX sed)だが、他の Linux ディストリビューションでは GNU の sed が利用されていることが多い。 両者はオプションの仕様や対応している正規表現に微妙に違いがあるため、同じコマンドを実行しても結果が一致しない場合がある。
個人の主観にはなるが、Google などで検索して出てくる情報は主に後者の GNU sed のものが多く、Mac で実行した場合は期待した出力にならないものがあるので注意が必要。
対処法
主な対処法は以下の2つ。
1. GNU sed をインストールして使う
GNU の sed は、Homebrew で gsed
をインストールすると利用可能。
% brew install gsed
通常のコマンドで GNU の sed を使いたい場合は、alias で指定してしまうとよい。
% alias sed='gsed'
2. 対応している記法に修正して使う
こちらに関してはどこまで差異があるのか全て把握できているわけではないが、現時点までに Mac の sed で実行してハマったものを記載しておく。
-i オプションを使うとエラーになってしまう
-i オプションは、入力として与えたファイルの内容を書き換えるオプションであるが、以下のように実行するとエラーになってしまう。
% cat hello.txt Hello, World! % sed -i s/World/Japan/g hello.txt sed: 1: "hello.txt": extra characters at the end of h command
Mac の sed で実行したい場合は、-i の後の引数にバックアップとして作成されるファイルの拡張子を指定する必要がある。sed コマンドで指定したファイルの内容は上書きされ、変更前の内容がバックアップファイルとして残される。
% sed -i .bak s/World/Japan/g hello.txt % cat hello.txt Hello, Japan! % cat hello.txt.bak Hello, World!
バックアップファイルを作成せずに上書きしたい場合は、-i の後の引数に空文字を指定するとよい。
% sed -i "" s/World/Japan/g hello.txt % cat hello.txt Hello, Japan!
正規表現の +
が期待通りにマッチしない
正規表現では +
を指定することで、「直前の表現を1回以上繰り返したもの」にマッチさせることができる。
しかし、Mac の sed だと +
が利用できず、使用しても期待通りに動いてくれない場合がある。
以下の例では B
が1個以上連続している箇所を B\+
*2 でマッチさせ、DDD
に書き換えようとしているが、Mac の sed では置換が起こっていない(GNU sed では置換される)。
% echo "AAA BBB BBBBB CCC" | sed -e "s/B\+/DDD/g" AAA BBB BBBBB CCC % echo "AAA BBB BBBBB CCC" | gsed -e "s/B\+/DDD/g" AAA DDD DDD CCC
+
ではなく、繰り返し回数指定の {MIN, MAX}
*3 の表現は利用できる。
MIN = 1, MAX = (なし) として利用すると同じような条件でマッチさせることができるので、こちらを使う。
% echo "AAA BBB BBBBB CCC" | sed -e "s/B\{1,\}/DDD/g" AAA DDD DDD CCC
まとめ
- Mac で標準になっている sed は BSD 系のもの(POSIX sed)だが、他の Linux ディストリビューションでは GNU の sed が利用されていることが多いため、動作に差がある場合がある。
- GNU の sed( gsed )を利用したい場合は Homebrew でインストールできる。
どのような差異があるかは全て把握するのは難しそうであるが、上記以外の差異が見つかったらまた記事にしていく予定。 ただ、目的の操作をしたい場合に情報量が多いのは GNU の sed かと思われるため、普段使う上では gsed を入れておくのが良いかと思う。