SED正規表現と非欲張りな一致(Perlの.*?エミュレート)

SED正規表現と非欲張りな一致(Perlの.*?エミュレート)

最初の文字列と2番目の文字列の間の文字sed列を置き換えるために使用したいと思います。AB最初発生AC(含む)XXX

~のためはい、次の文字列があります(この文字列はテストにのみ使用されます)。

ssABteAstACABnnACss

私は次のような出力が欲しいssXXXABnnACss


私はこれを使ってこれをしましたperl

$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss

しかし、私はそれを達成するためにそれを使用したいと思いますsed。次(Perl互換正規表現を使用)は機能しません。

$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss

答え1

Sed 正規表現は、最も長い一致と一致します。 Sedはnon-greedyと同等の機能はありません。

私たちがしなければならないことは一致するものです

  1. AB
    続いて
  2. 除くすべてのAC数量
  3. AC

残念ながらsed#2は実行できません。少なくとも複数文字の正規表現では実行できません。もちろん、単一文字の正規表現@()の場合はこれまたはを[123]実行できます。したがって、sedのすべての項目を変更してから検索してsedの制限を解決できます。[^@]*[^123]*AC@

  1. AB
    続いて
  2. @除く
    すべて
  3. @

このように:

sed 's/AC/@/g; s/AB[^@]*@/XXX/; s/@/AC/g'

最後の部分は一致しない@バックインスタンスをAC

ただし、入力にすでに文字が含まれている可能性があるため、これは無謀なアプローチです@。だからそれらを一致させることで、私たちは偽の肯定を得ることができます。ただし、NUL()文字はシェル変数に含まれていないため、\x00NULは上記の回避策で代わりに使用するのに最適な文字かもしれません@

$ echo 'ssABteAstACABnnACss' | sed 's/AC/\x00/g; s/AB[^\x00]*\x00/XXX/; s/\x00/AC/g'
ssXXXABnnACss

NULを使用するにはGNU sedが必要です。 (GNU機能を有効にするには、ユーザーはシェル変数POSIXLY_CORRECTを設定しないでください。)

-zNULで区切られた入力(出力など)を処理するためにGNUフラグと一緒にsedを使用すると、find ... -print0NULはパターンスペースには表示されず、NULは置き換えるのに最適です。

printfNULはbash変数には表示できませんが、コマンドに含めることができます。入力文字列にNULを含む任意の文字を含めることができる場合は、次を参照してください。Stefan Chazerasの答えこれはきちんとしたエスケープ方法を追加します。

答え2

非欲望的なマッチングを行う単一文字、一致を終了する文字を除くすべての文字と一致します。

グリディマッチング:

$ echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

貪欲ではないマッチング:

$ echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar

源泉:sed - Christoph Sieghartの貪欲なマッチング

答え3

一部のsed実装ではこれをサポートしています。ssedPCREモードがあります:

ssed -R 's/AB.*?AC/XXX/'

AT&T AST sed*?貪欲ではないバージョンで演算子をサポートします。*拡大する(と-E)と改善-A正規表現を使用)。

sed -E 's/AB.*?AC/XXX/'
sed -A 's/AB.*?AC/XXX/'

この実装およびより一般的には、対応する-E/-Aパターンでは、Perlに似た正規表現を内部的に使用できます(?P:perl-like regexp here)。ただし、上記のように演算子にはこれは必要ありません*?

それ改善正規表現には結合演算子と否定演算子もあります。

sed -A 's/AB(.*&(.*AC.*)!)AC/XXX/'

移植可能な方法は、以下の技術を使用することができる。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/; # actual replacement
     s/:/AC/g; # restore the remaining end strings
     s/_c/:/g; # revert escaping
     s/_u/_/g'

GNUの場合、sed1つのアプローチは改行文字を代替文字として使用することです。一度に1行ずつ処理するため、sedパターンスペースに改行文字が表示されないため、次のようにできます。

sed 's/AC/\n/g;s/AB[^\n]*\n/XXX/;s/\n/AC/g'

sedこれは通常、他の実装ではサポートされていないため機能しません[^\n]。 GNUの場合は、sedPOSIX互換性が有効になっていないことを確認する必要があります(たとえば、POSIXLY_CORRECT環境変数を使用)。

答え4

解決策はとても簡単です。 .*貪欲になるが、完全に貪欲をささげないでください。ssABteAstACABnnACssregexpと一致することを検討してくださいAB.*ACAC次の内容は実際に.*一致する必要があります。問題は貪欲なので、その後.*AC試合最後 AC最初よりも。正規表現のリテラルがssABteAstACABnnの最後のリテラルと一致している間に.*最初のものを食べます。ACAC交流春夏シーズン。これが発生しないようにするには、最初のものをAC別のものに置き換えます。言わない2番目と他のすべてを区別します。

echo ssABteAstACABnnACss | sed 's/AC/-foobar-/; s/AB.*-foobar-/XXX/'
ssXXXABnnACss

.*これと正規表現以外には何もないので、貪欲は今inの足元で止まります。-foobar-ssABteAst-foobar-ABnnACss-foobar--foobar--foobar- 〜しなければならないゲームをしよう以前の問題は、正規表現に2つの一致がありましたが、欲望のためにAC最後の一致が選択されたことでした。しかし、この場合、一度だけ一致することが可能であり、この一致はこれが決して貪欲ではないことを証明します。バス停は以下のエリアでのみ表示されます。.*AC-foobar-.*.*一つ次の正規表現の残りの部分はまだ一致します.*

ACABエラーがAC置き換えられるため、この回避策が最初の解決策よりも前に表示されると失敗します-foobar-。たとえば、最初の置換sedの後にACssABteAstACABnnACssなります-foobar-ssABteAstACABnnACss。したがって、一致するものが見つかりませんAB.*-foobar-。ただし、シーケンスが常に...AB...AC...AB...AC... の場合、このソリューションは成功します。

関連情報