ソリューション:
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'