シェルスクリプトでsedを使いカッコやダブルクォートで囲まれた部分のみ一括置換したい

2021-03-12Bash

はじめに

タイトルで説明しにくいのが辛い(-_-;)

僕も常々 sed で シングルクォートやダブルクォートで囲まれた部分のみ置換したいが、なんとかできないものか?と思っていましたので、紹介したいと思います。

検証環境

$ sed --version | head -n 1
sed (GNU sed) 4.7

やりたいこと

以下のような文字列があるとします。

私は、いろいろな国(日本、中国、韓国)に行ったことがあります。

これを以下のように置換したいと思います。

私は、いろいろな国(日本|中国|韓国)に行ったことがあります。

括弧の中の だけを | に置換したいわけです。

seds 内部コマンドだけでは実現は難しい…

ぱっと思いつくのが sed 's/、/|/g' ですね。

$ echo '私は、いろいろな国(日本、中国、韓国)に行ったことがあります。' \
  | sed 's/、/|/g'
私は|いろいろな国(日本|中国|韓国)に行ったことがあります。

これだと 括弧の内側以外も置換されて しまいます。

$ echo '私は、いろいろな国(日本、中国、韓国)に行ったことがあります。' \
  | sed 's/(\([^、)]*\)、\([^)]*\))/(\1|\2\)/g'
私は、いろいろな国(日本|中国、韓国)に行ったことがあります。

これだと、カッコ内の1つ目の しか置換できません。

seds 内部コマンドとほかの内部コマンドを組み合わせる

: 内部コマンドと t 内部コマンドを組み合わせると、標準出力から受け取った文字列に s 内部コマンドの置換処理が適用できなくなるまで処理を繰り返すことができます。

  • : 内部コマンド → ラベルを作成
  • t 内部コマンド → s 内部コマンドが成功していたら、 : で付与したラベルまで遡る
$ echo '私は、いろいろな国(日本、中国、韓国)に行ったことがあります。' \
  | sed ':q; s/(\([^、)]*\)、\([^)]*\))/(\1|\2)/g; t q'
私は、いろいろな国(日本|中国|韓国)に行ったことがあります。

ひとこと

括弧だけでなく、 ダブルクォートで囲まれた部分のみ一括置換したいシングルクォーテーションで囲まれた部分のみ一括置換したい といったケースはよくあります。

コードの文字列リテラル部分だけを置換したい!といったケースに活躍するのではないでしょうか。

2021-03-12Bash