「grep-zoP」ですべての一致を個別に表示するにはどうすればよいですか?

Nov 23 2020

私はこのフォームにファイルを持っています:

X/this is the first match/blabla
X-this is
the second match-

and here we have some fluff.

そして、「X」の後と同じマーカーの間に表示されるすべてのものを抽出したいと思います。したがって、「X + match +」がある場合、「X」の後、マーカー「+」の間に表示されるため、「match」を取得したいと思います。

したがって、指定されたサンプルファイルについて、次の出力が必要です。

this is the first match

その後

this is
the second match

Xとそれに続くマーカーの間のすべてのコンテンツを、次を使用して取得することができました。

grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file

あれは:

  • grep -Po '(?<=X(.))(.|\n)+(?=\1)'X(something)と一致し、その後にそれがキャプチャされ、最後にと一致します(ここでの回答(?=\1)に基づいてコードを作成しました)。
  • (.|\n)は新しい行を含むすべてのものを照合するために使用し-z、grepでも新しい行を照合するために使用することに注意してください。

したがって、これはうまく機能します。唯一の問題は、出力の表示にあります。

$ grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file
this is the first matchthis is
the second match

ご覧のとおり、すべての一致が一緒に表示され、「これが最初の一致です」の後に「これが2番目の一致です」が続き、区切り文字はまったくありません。これは、すべてのファイルを改行(「mangrep」を引用)ではなくゼロバイト(ASCII NUL文字)で終了する一連の行として扱う「-z」の使用法に由来することを私は知っています

だから:これらすべての結果を別々に取得する方法はありますか?

GNUAwkでも試しました。

awk 'match($0, /X(.)(\n|.*)\1/, a) {print a[1]}' file

しかし、うまくいきませんでした(\n|.*)

回答

2 tripleee Nov 23 2020 at 20:23

一致を印刷するとすぐに、セパレータが正確にどこにあったかに関する情報が失われるため、ユースケースは一種の問題があります。しかし、それが許容できる場合は、に配管してみてくださいxargs -r0

grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file | xargs -r0

これらのオプションはGNU拡張機能ですが、そうですgrep -z(ほとんど)grep -Pので、おそらくそれは許容できます。

5 Sundeep Nov 23 2020 at 20:16

awk 正規表現定義内の後方参照をサポートしていません。

回避策:

$ grep -zPo '(?s)(?<=X(.)).+(?=\1)' ip.txt | tr '\0' '\n' this is the first match this is the second match # with ripgrep, which supports multiline matching $ rg -NoUP '(?s)(?<=X(.)).+(?=\1)' ip.txt
this is the first match
this is
the second match

(?s)X(.)\K.+(?=\1)代わりに使用することもできます(?s)(?<=X(.)).+(?=\1)。また、match+xyz+foobaz入力との一致を避けるために、ここで貪欲でない数量詞を使用することもできますX+match+xyz+foobaz+


perl

$ perl -0777 -nE 'say $& while(/X(.)\K.+(?=\1)/sg)' ip.txt
this is the first match
this is
the second match
4 anubhava Nov 23 2020 at 22:21

これは、RSとを利用した別のgnu-awkソリューションRTです。

awk -v RS='X.' 'ch != "" && n=index($0, ch) { print substr($0, 1, n-1)
}
RT {
   ch = substr(RT, 2, 1)
}' file
this is the first match
this is
the second match
3 EdMorton Nov 23 2020 at 21:51

複数文字のRS、RT、およびgensub()用のGNU awkを使用し、ファイル全体をメモリに読み込む必要はありません。

$ awk -v RS='X.' 'NR>1{print "<" gensub(end".*","",1) ">"} {end=substr(RT,2,1)}' file
<this is the first match>
<this is
the second match>

明らかに、各出力レコードの開始/終了を確認できるように、「<」と「>」を追加しました。

上記の文字の後にあることを前提とX非繰り返し正規表現metachar(例えばないが.^[、など)ので、メーリングリストへ

1 rowboat Nov 24 2020 at 10:09

GNUgrep -zは、入出力レコードをnull文字で終了します(などの他のツールと組み合わせて使用​​すると便利ですsort -z)。pcregrepはそれを行いません:

pcregrep -Mo2 '(?s)X(.)(.+?)\1' file

-onumber used instead of lookarounds. ? lazy quantifier added (in case \1 occurs later).