sedで特定の文字を二重エスケープする必要があるのはなぜですか?

sedで特定の文字を二重エスケープする必要があるのはなぜですか?

これは問題を示すテストファイルだけです。元の部分は次のようになりました。

arch systemd[908]:

これで、閉じ括弧を「:」文字に置き換えることができます。

krys@archeos:~/test]$ cat jctl.log | cut -d " " -f 4,5 | head | sed s/]/:/g
arch systemd[908::

開いているブラケットを交換しようとすると機能しません。

krys@archeos:~/test]$ cat jctl.log | cut -d " " -f 4,5 | head | sed s/[/:/g
sed: -e expression #1, char 7: unterminated `s' command

その後、1\文字で「[」をエスケープしましたが、まだ機能しません。

krys@archeos:~/test]$ cat jctl.log | cut -d " " -f 4,5 | head | sed s/[/:/g
sed: -e expression #1, char 7: unterminated `s' command

2つの「\」を使用してください。

krys@archeos:~/test]$ cat jctl.log | cut -d " " -f 4,5 | head | sed s/\\[/:/g
arch systemd:908]:

私の質問は次のとおりです。

  • 右ブラケットでは機能しますが、左ブラケットでは機能しないのはなぜですか? sedまたはbashがこれを読む方法の違いは何ですか?
  • 右角かっことうまく機能し、まったくエスケープする必要はありませんが、左角かっこを2回エスケープする必要があるのはなぜですか?

私はこれを理解したいと思います。これでこれを行う方法を知っていますが、詳細を知らないことに満足していません。

答え1

sedこれはsed式を引用しなかったためです(悪い考えです。コマンドを一重引用符で囲む習慣があります)。エスケープする必要があるのは、正規表現で特別な意味を持ち、文字クラスを開くこと[です(たとえば、またはいずれかと一致します)。 sedは以前に閉じることがないので、この場合、文字クラスが閉じられないことを知るほどスマートなので、脱出する必要はありません。[[abc]abc]][

これはsed式を引用していないので、これはシェルがそれを解釈しようとすることを意味します。今後に渡しますsed。したがって、シェルはユーザーの文字を表示し、\[エスケープされていない文字をsedに渡します。これにより、これを実際に確認できますset -x

$ set -x
$ sed s/\[/:/g jctl.log
+ sed 's/[/:/g' jctl.log
sed: -e expression #1, char 7: unterminated `s' command

ご覧のとおり、実行される実際のコマンドはsed 's/[/:/g' jctl.log次のとおりですsed 's/\[/:/g' jctl.log。シェルはエスケープ文字を使用しました。その後、sedはターミネータを探していますが、見つから]ないため失敗します。したがって、文字列全体を/:/g文字クラスの内容として扱うため、コマンドの終わりが見つからないため失敗しますs///

2番目のエスケープレベルを追加すると、シェルは最初のエスケープレベルを使用してから、まだエスケープを[sedに渡すことができます。

$ sed s/\\[/:/g jctl.log
+ sed 's/\[/:/g' jctl.log
arch systemd:908]:

上記の出力から、\[sedの代わりにsedが提供されていることがわかります[

常に sed コマンドを引用すると、次の問題はすべて消えます。

$ sed 's/\[/:/g' jctl.log
arch systemd:908]:

関連情報