sed
を使用して、AB
の最初のAC
とfirstの間にある文字列のすべてをXXX
で置き換えます。
exampleの場合、次の文字列があります(この文字列はテスト専用です):
ssABteAstACABnnACss
そして、私はこれに似た出力を望みます:ssXXXABnnACss
。
私はPerl
でこれを行いました:
$ echo 'ssABteAstACABnnACss' | Perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss
しかし、sed
で実装したいと思います。以下は(Perl互換の正規表現を使用して)機能しません。
$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss
Sed正規表現は最長一致に一致します。 Sedには、貪欲でないものに相当するものはありません。
明らかに私たちがしたいのは試合です
AB
、AC
以外の任意の量AC
残念ながら、sed
は#2を実行できません—少なくとも複数文字の正規表現ではできません。もちろん、@
(または[123]
)のような単一文字の正規表現の場合、[^@]*
または[^123]*
を実行できます。そして、sedの制限を回避するには、AC
の出現箇所をすべて@
に変更してから、
AB
、@
以外の任意の数@
このような:
sed 's/AC/@/g; s/AB[^@]*@/XXX/; s/@/AC/g'
最後の部分は、@
の一致しないインスタンスをAC
に戻します。
しかし、もちろん、これは無謀なアプローチです。入力には既に@
文字が含まれている可能性があるため、それらを照合することで、誤検知が発生する可能性があります。ただし、シェル変数にはNUL(\x00
)文字が含まれないため、上記の回避策では@
の代わりにNULを使用することをお勧めします。
$ echo 'ssABteAstACABnnACss' | sed 's/AC/\x00/g; s/AB[^\x00]*\x00/XXX/; s/\x00/AC/g'
ssXXXABnnACss
NULを使用するにはGNU sedが必要です(GNU機能が有効であることを確認するには、ユーザーがシェル変数POSIXLY_CORRECTを設定していない必要があります。)
GNUの-z
フラグでsedを使用して、NULで区切られた入力(find ... -print0
の出力など)を処理する場合、NULはパターンスペースにないため、NULをここでの置換に適しています。 。
NULはbash変数に含めることはできませんが、printf
コマンドに含めることができます。入力文字列にNULを含むすべての文字を含めることができる場合は、 StéphaneChazelasの回答 を参照してください。これにより、巧妙なエスケープメソッドが追加されます。
一部のsed
実装は、それをサポートしています。 ssed
にはPCREモードがあります:
ssed -R 's/AB.*?AC/XXX/g'
AT&T ast sed は、拡張正規表現を使用する場合、結合と否定があります:
sed -A 's/AB(.*&(.*AC.*)!)AC/XXX/g'
移植性の高い方法で、この手法を使用できます。終了文字列(ここではAC
)を、開始文字列にも終了文字列にも発生しない単一文字(ここでは:
など)に置き換えます。 s/AB[^:]*://
、およびその文字が入力に現れる可能性がある場合は、開始文字列と終了文字列と衝突しないエスケープメカニズムを使用します。
例:
sed 's/_/_u/g; # use _ as the escape character, escape it
s/:/_c/g; # escape our replacement character
s/AC/:/g; # replace the end string
s/AB[^:]*:/XXX/g; # actual replacement
s/:/AC/g; # restore the remaining end strings
s/_c/:/g; # revert escaping
s/_u/_/g'
GNU sed
の場合、アプローチは、改行を置換文字として使用することです。sed
は一度に1行を処理するため、パターンスペースで改行が発生することはありません、そうすることができます:
sed 's/AC/\n/g;s/AB[^\n]*\n/XXX/g;s/\n/AC/g'
他のsed
実装は[^\n]
をサポートしていないため、通常、これは機能しません。 GNU sed
を使用して、POSIX互換性が有効になっていないことを確認する必要があります(POSIXLY_CORRECT環境変数の場合など)。
いいえ、sed正規表現には貪欲でない一致はありません。
最初に出現するAC
までのすべてのテキストを一致させるには、「AC
を含まないもの」の後にAC
を続けます。これは、Perlの.*?AC
と同じです。 。問題は、「AC
を含まないもの」は正規表現として簡単に表現できないことです。正規表現の否定を認識する正規表現は常に存在しますが、否定の正規表現は複雑になります。ポータブルsedでは、これは不可能です。否定正規表現では、拡張正規表現(たとえばawk)に存在するが、ポータブル基本正規表現には存在しない代替をグループ化する必要があるためです。 GNU sedなど)の一部のバージョンには、可能なすべての正規表現を表現できるようにするBREへの拡張機能があります。
sed 's/AB\([^A]*\|A[^C]\)*A*AC/XXX/'
正規表現を否定するのは難しいため、これは一般化されません。代わりにできることは、ラインを一時的に変形することです。一部のsed実装では、改行を入力行に表示できないため、改行をマーカーとして使用できます(複数のマーカーが必要な場合は、改行の後にさまざまな文字を続けます)。
sed -e 's/AC/\
&/g' -e 's/AB[^\
]*\nAC/XXX/' -e 's/\n//g'
ただし、一部のsedバージョンの文字セットでは、バックスラッシュと改行が機能しないことに注意してください。特に、これはGNU sed、非組み込みLinuxでのsed実装)では機能しません。GNU sedでは、\n
代わりに:
sed -e 's/AC/\
&/g' -e 's/AB[^\n]*\nAC/XXX/' -e 's/\n//g'
この特定のケースでは、最初のAC
を改行で置き換えるだけで十分です。上記のアプローチはより一般的です。
Sedのより強力なアプローチは、ラインをホールドスペースに保存し、ラインの最初の「興味深い」部分以外をすべて削除し、ホールドスペースとパターンスペースを交換するか、パターンスペースをホールドスペースに追加して繰り返すことです。ただし、これほど複雑なことを始める場合は、awkへの切り替えを検討する必要があります。 Awkにも貪欲なマッチングはありませんが、文字列を分割して、パーツを変数に保存できます。
sed-Christoph Sieghartによる貪欲でない一致
Sedで貪欲でない一致を取得するコツは、一致を終了させる文字を除くすべての文字を一致させることです。言うまでもありませんが、貴重な時間を無駄にしましたが、結局のところ、シェルスクリプトは迅速かつ簡単なはずです。したがって、他の誰かがそれを必要とする可能性がある場合:貪欲なマッチング
% echo "<b>foo</b>bar" | sed 's/<.*>//g' bar
貪欲でないマッチング
% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g' foobar
代替手段の1つは、文字列を変更することですwant貪欲な一致
echo "ssABtCeCAstACABnnACss" | rev | sed -E "s/(.*)CA.*BA(.*)/\1CA+-+-+-+-BA\2/" | rev
rev
を使用して文字列を逆にし、一致基準を逆にし、sed
を通常の方法で使用してから、結果を逆にします。..
ssAB-+-+-+-+ACABnnACss
解決策は非常に簡単です。 .*
は貪欲ですが、完全に貪欲というわけではありません。正規表現AB.*AC
に対してssABteAstACABnnACss
を照合することを検討してください。 .*
に続くAC
は、実際に一致する必要があります。問題は、.*
が貪欲であるため、後続のAC
が最初のものではなくlastAC
と一致することです。 .*
は最初のAC
を消費しますが、正規表現のリテラルAC
はssABteAstACABnn [〜#〜] ac [〜#〜]の最後のリテラルと一致します= ss。これが起こらないようにするには、最初のAC
を何かとんでもないに置き換えるだけで、2番目のものや他のものと区別できます。
echo ssABteAstACABnnACss | sed 's/AC/-foobar-/; s/AB.*-foobar-/XXX/'
ssXXXABnnACss
貪欲な.*
は-foobar-
のssABteAst-foobar-ABnnACss
の足元で停止します。これは、この-foobar-
以外に-foobar-
がないため、正規表現-foobar-
[〜#〜]必須[〜#〜]一致する。以前の問題は、正規表現AC
に2つの一致があったことですが、.*
が貪欲だったため、AC
の最後の一致が選択されました。ただし、-foobar-
を使用すると、1つの一致のみが可能であり、この一致は.*
が完全に貪欲ではないことを証明します。 .*
のバス停は、.*
に続く残りの正規表現でoneの一致のみが残っている場合に発生します。
間違ったAC
が-foobar-
に置き換えられるため、最初のAB
の前にAC
が表示されると、このソリューションは失敗することに注意してください。たとえば、最初のsed
置換の後、ACssABteAstACABnnACss
は-foobar-ssABteAstACABnnACss
になります。したがって、AB.*-foobar-
に対する一致は見つかりません。ただし、シーケンスが常に... AB ... AC ... AB ... AC ...の場合、このソリューションは成功します。
あなたの場合は、次のようにして閉じる文字を無効にすることができます:
echo 'ssABteAstACABnnACss' | sed 's/AB[^C]*AC/XXX/'