ソリューション:
echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'
以前の試みがうまくいかなかった理由:
.*
は貪欲であるため、(foo).*(abc)?.*(bar)
一致しようとする正規表現'foo_abc_bar'
は(foo)
に一致し'foo'
、.*
最初に文字列の残りの部分に一致します ( '_abc_bar'
)。正規表現は、必要なグループに到達するまで続行されますが、(bar)
これは失敗します。その時点で、正規表現は、.*
. これは、最初のグループが一致.*
するだけになるまで発生'_abc_'
し、その時点で最後のグループが一致する可能性があり'bar'
ます。したがって'abc'
、文字列の がキャプチャ グループで一致するのではなく、非キャプチャで一致し.*
ます。
私の解決策の説明:
最初で最も重要なことは、 を に置き換える.*
こと_
です。セパレータが何であるかがわかっている場合は、任意の文字列に一致させる必要はありません。次に行う必要があるのは、文字列のどの部分が省略可能かを正確に把握することです。文字列'foo_abc_bar'
と'foo_bar'
が両方とも有効な場合'abc_'
、真ん中はオプションです。を使用して、これをオプションのグループに入れることができます(abc_)?
。最後のステップは'abc'
、キャプチャ グループに文字列がまだ残っていることを確認することです。これは、その部分を追加のグループでラップすることで実行できるため、((abc)_)?
. 次に、余分なグループがあるため、置換を調整する必要があります。\1=\2=\3
使用する代わりに、文字列になり\1=\3=\4
ます\2
'abc_'
(一致した場合)。ほとんどの正規表現の実装では、非キャプチャ グループを使用し、引き続き を使用することもできまし\1=\2=\3
たが、sed は非キャプチャ グループをサポートしていないことに注意してください。
別の方法:
上記の正規表現は最も明示的であるため、最善の策だと思います(関心のある正確な文字列にのみ一致します)。ただし、貪欲な繰り返し (できるだけ多くの文字に一致) の代わりに、遅延繰り返し (できるだけ少ない文字に一致) を使用することで、上記の問題を回避することもできます。.*
を toに変更することでこれを行うことができる.*?
ため、式は次のようになります。
echo 'foo_abc_bar' | sed -r 's/(foo).*?(abc).*?(bar)/\1=\2=\3/g'