web-dev-qa-db-ja.com

複数行にわたってパターンを「grep」するにはどうすればよいですか?

grep/egrepを誤用しているようです。

複数行で文字列を検索しようとしていましたが、探しているものが一致しているはずなのに、一致するものが見つかりませんでした。もともと私は正規表現が間違っていると思っていましたが、最終的にはこれらのツールが1行ごとに動作することを読みました(私の正規表現も非常に簡単だったため、問題にはなりませんでした)。

では、複数の行にまたがってパターンを検索するには、どのツールを使用するのでしょうか。

30
Jim

これは、複数行にわたってsedのような動作を提供するgrepの1つです。

sed -n '/foo/{:start /bar/!{N;b start};/your_regex/p}' your_file

仕組み

  • -nは、すべての行を印刷するデフォルトの動作を抑制します
  • /foo/{}は、fooに一致するように指示し、一致する行に波線の内側にある処理を実行します。 fooをパターンの開始部分に置き換えます。
  • :startは、正規表現の終わりを見つけるまでループを続けるのに役立つ分岐ラベルです。
  • /bar/!{}は、barと一致しない行に対して波線にあるものを実行します。 barをパターンの終了部分に置き換えます。
  • Nは次の行をアクティブなバッファーに追加します(sedはこれをパターンスペースと呼びます)
  • b startは、パターンスペースにstartが含まれていない限り、次の行を追加し続けるために、前に作成したbarラベルに無条件に分岐します。
  • /your_regex/pは、your_regexと一致する場合にパターンスペースを出力します。 your_regexは、複数行にわたって照合する式全体に置き換える必要があります。
29
Joseph R.

私は通常、pcregrepというツールを使用します。このツールは、yumまたはaptを使用して、ほとんどのLinuxフレーバーにインストールできます。

たとえば.

testfileという名前のファイルにコンテンツが含まれているとします

abc blah
blah blah
def blah
blah blah

次のコマンドを実行できます。

$ pcregrep -M  'abc.*(\n|.)*def' testfile

複数行にわたってパターンマッチングを行う。

さらに、sedでも同じことができます。

$ sed -e '/abc/,/def/!d' testfile
21
pradeepchhetri

Perlを使用したより簡単なアプローチを次に示します。

_Perl -e '$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m' file
_

または(JosephR sedルートを取得したため 、私は恥ずかしくないように彼を盗みます 提案

_Perl -n000e 'print $& while /^foo.*\nbar.*\n/mg' file
_

説明

$f=join("",<>);:ファイル全体を読み取り、その内容(改行とすべて)を変数_$f_に保存します。次に、_foo\nbar.*\n_を照合し、一致した場合は出力します(特殊変数_$&_は、最後に見つかった一致を保持します)。 _///m_は、改行全体で正規表現を一致させるために必要です。

_-0_は、入力レコード区切り記号を設定します。これを_00_に設定すると、「段落モード」がアクティブになり、Perlは連続する改行(_\n\n_)をレコード区切りとして使用します。連続する改行がない場合、ファイル全体が一度に読み取られます(丸められます)。

警告:

大きなファイルに対してこれを行うしないと、ファイル全体がメモリに読み込まれ、問題が発生する可能性があります。

6
terdon

これを行う1つの方法は、Perlを使用することです。例えばfooという名前のファイルの内容は次のとおりです。

_foo line 1
bar line 2
foo
foo
foo line 5
foo
bar line 6
_

さて、fooで始まるすべての行と、barで始まるすべての行が続くPerlを次に示します。

_cat foo | Perl -e 'while(<>){$all .= $_}
  while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) {
  print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m;
}'
_

分解されたPerl:

  • while(<>){$all .= $_}標準入力全体を変数_$all_にロードします
  • _while($all =~_変数allには正規表現があります...
  • /^(foo[^\n]*\nbar[^\n]*\n)/m正規表現:行の先頭にあるfooの後に任意の数の非改行文字が続き、その後に改行が続き、直後に "bar"が続き、残りの行にbarが含まれます。正規表現の最後の_/m_は、「複数行にわたって一致する」ことを意味します
  • _print $1_かっこ内にあった正規表現の部分(この場合は、正規表現全体)を出力します
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m正規表現の最初の一致を消去して、問題のファイル内の正規表現の複数のケースを照合できるようにします

そして出力:

_foo line 1
bar line 2
foo
bar line 6
_
3
samiam

Grepの代替 ​​sift は複数行のマッチングをサポートします(免責事項:私は作成者です)。

testfileに次のものが含まれているとします:

 <book> 
 <title> Lorem Ipsum </ title> 
 <description> Lorem ipsum dolor sit amet、consectetur 
 adipiscing elit、sed do eiusmod tempor incididunt ut 
 Labor et dolore magna aliqua </ description> 
 </ book> 


_sift -m '<description>.*?</description>'_(説明を含む行を表示)

結果:

 testfile:<description> Lorem ipsum dolor sit amet、consectetur 
 testfile:adipiscing elit、sed do eiusmod tempor incididunt ut 
 testfile:Labor et dolore magna aliqua </ description> 


sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename(説明を抽出して再フォーマット)

結果:

description = "Lorem ipsum dolor sit amet、consectetur 
 adipiscing ellit、sed do eiusmod tempor incididunt ut 
 Labor et dolore magna aliqua" 
2
svent

Perl-regexpパラメータPをサポートする通常のgrepがこの仕事をします。

$ echo 'abc blah
blah blah
def blah
blah blah' | grep -oPz  '(?s)abc.*?def'
abc blah
blah blah
def

(?s)はDOTALL修飾子を呼び出し、正規表現のドットを文字だけでなく改行にも一致させます。

2
Avinash Raj

test.txtを含むファイルがあるとします。

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

次のコードを使用できます。

sed -n '/foo/,/bar/p' test.txt

次の出力の場合:

foo
here
is the
text
to keep between the 2 patterns
bar

別のgrepでgrepと-Aオプションを使用してこれを解決しました。

grep first_line_Word -A 1 testfile | grep second_line_Word

-A 1オプションは、見つかった行の後に1行印刷します。もちろん、ファイルとWordの組み合わせによって異なります。しかし、私にとっては、これが最速で信頼できるソリューションでした。

1
mansur

自分自身を除く2つのパターン間のテキストを取得する場合。

以下を含むファイルtest.txtがあるとします。

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

次のコードを使用できます。

 sed -n '/foo/{
 n
 b gotoloop
 :loop
 N
 :gotoloop
 /bar/!{
 h
 b loop
 }
 /bar/{
 g
 p
 }
 }' test.txt

次の出力の場合:

here
is the
text
to keep between the 2 patterns

それがどのように機能するか、それを段階的に作りましょう

  1. /foo/{は、行に「foo」が含まれている場合にトリガーされます
  2. nパターンスペースを次の行、つまり「ここ」という単語で置き換えます
  3. b gotoloopラベル「gotoloop」への分岐
  4. :gotoloopはラベル「gotoloop」を定義します
  5. /bar/!{パターンに「バー」が含まれていない場合
  6. hホールドスペースをパターンに置き換えるため、「ここ」がホールドスペースに保存されます
  7. b loopラベル「loop」への分岐
  8. :loopはラベル「ループ」を定義します
  9. Nは、パターンをホールドスペースに追加します。
    現在の保留スペースには以下が含まれます:
    "ここに"
    "それは"
  10. :gotoloopこれで手順4になり、行に「bar」が含まれるまでループします
  11. /bar/ループが終了し、「バー」が見つかりました。これはパターンスペースです
  12. gパターンスペースは、メインループ中に保存された「foo」と「bar」の間のすべての行を含むホールドスペースに置き換えられます
  13. pパターンスペースを標準出力にコピーします

できた!

sedmultilineloop