ShellScript
Linux
sed

sed -ie "s/ho/ge/" filename は意図通りではないかもしれない

More than 1 year has passed since last update.

rm -rf
のように-つき一文字オプションはつなげて書けたりかけなかったりする。
sedはいろんな意味でややこしい。
sed -ie "s/ho/ge/" filename
は一見うまくいくが、よく見ると意図した動きにはなっていない。

意図通りのコマンド

sed -i -e "s/ho/ge/" filename

オプションをちゃんと分けて書く。

sed -ie "s/ho/ge/" filenameの動き

filenameは置換された内容で上書きされ、
置換前のバックアップがfilenameeに作成される。
filenameだけ見ていると正常に上書きされているので成功しているが、意図していないバックアップファイルが作成されている。

-ieとは何か

前述の結果から予測できるでしょうが、iの後のファイル名を付けてバックアップをとる。
-i.bakなどとされる。

-i[SUFFIX], --in-place[=SUFFIX]
ファイルをインプレース処理で編集する (SUFFIX 指定時はバックアップを取る)

Man page of SED

-i[SUFFIX], --in-place[=SUFFIX]
edit files in place (makes backup if SUFFIX supplied)

,

$ sed -i.bak -e 's/[11一]週間/1週間/g' dummy.txt
なんと、-iオプションに続いてバックアップファイルの接尾名を付けるだけ。

sedだけでバックアップをとりつつ安全に上書き置換する - Qiita

# 上書き前のファイルに拡張子.bakをつけて保管することもできる
sed -i.bak -e '/^#/d' config.txt

sedでこういう時はどう書く? - Qiita

↑記事のコメントやらリンクをたどると結構互換性とかの話が出てきたり、バックアップのミスに一瞬気づかなかったりな小さなつまづきは案外あるのかもしれない。

sedの-iオプションの非互換 - Qiita

Linux(GNU)とMac(BSD)のsedの振る舞いの違いを解決 - Qiita

-ieでなぜ正規表現の置換上書きが成功するのか

eがオプションではなくバックアップのサフィックスになることは理解できた。
これで変なファイルが作成された謎は解決。
だが、-eが無いにも関わらず正規表現の置換が出来た原因が謎である。
処理を書くなら-eとかしこで見た。

でももうちょっとだけちゃんと解説を読むと理解できた。

-e、--expression、-f、または --file オプションのいずれも指定されない場合、最初のオプションでない引き数が sed スクリプトとして解釈される。残りの全ての引き数は入力ファイル名として扱われる。入力ファイルが指定されない場合は、標準入力から読み込む。

つまり、-eはスクリプトの場所を-eの後ろの引数だと明示するためのものであり、スクリプトを引数で使う際に必須ではないということでした。
この例ではオプションでない最初の引数はスクリプトになっているので、-eがなくても正しくスクリプトを読み込んでいたということ。

つまり…

sed -ei "s/ho/ge/" filename

sed: -e expression #1, char 1: expected \ after `a', `c' or `i'

-eのあとのiがスクリプトと解釈され、構文エラー?

sed -e -i "s/ho/ge/" filename

sed: -e expression #1, char 1: unknown command: `-'

-eのあとの-iがスクリプトと解釈され、構文エラー?


正常な
sed -i -e
形式のありえそうな誤表記3つのうち唯一エラーが出ず、
一見意図通り動いている形式を見事に踏み抜いて悩んでいたのか…

エラーが出ずとも、対象ファイルだけではなくディレクトリ全体を確認したり、オプションはズボらずにちゃんと書きましょう?で、教訓はいいのか…?
レアケースでいいかな?