grep
/egrep
を誤用しているようです。
複数行で文字列を検索しようとしていましたが、探しているものが一致しているはずなのに、一致するものが見つかりませんでした。もともと私は正規表現が間違っていると思っていましたが、最終的にはこれらのツールが1行ごとに動作することを読みました(私の正規表現も非常に簡単だったため、問題にはなりませんでした)。
では、複数の行にまたがってパターンを検索するには、どのツールを使用するのでしょうか。
これは、複数行にわたって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
は、複数行にわたって照合する式全体に置き換える必要があります。私は通常、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
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
_)をレコード区切りとして使用します。連続する改行がない場合、ファイル全体が一度に読み取られます(丸められます)。
大きなファイルに対してこれを行うしないと、ファイル全体がメモリに読み込まれ、問題が発生する可能性があります。
これを行う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
_
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"
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修飾子を呼び出し、正規表現のドットを文字だけでなく改行にも一致させます。
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の組み合わせによって異なります。しかし、私にとっては、これが最速で信頼できるソリューションでした。
自分自身を除く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
それがどのように機能するか、それを段階的に作りましょう
/foo/{
は、行に「foo」が含まれている場合にトリガーされますn
パターンスペースを次の行、つまり「ここ」という単語で置き換えますb gotoloop
ラベル「gotoloop」への分岐:gotoloop
はラベル「gotoloop」を定義します/bar/!{
パターンに「バー」が含まれていない場合h
ホールドスペースをパターンに置き換えるため、「ここ」がホールドスペースに保存されますb loop
ラベル「loop」への分岐:loop
はラベル「ループ」を定義しますN
は、パターンをホールドスペースに追加します。:gotoloop
これで手順4になり、行に「bar」が含まれるまでループします/bar/
ループが終了し、「バー」が見つかりました。これはパターンスペースですg
パターンスペースは、メインループ中に保存された「foo」と「bar」の間のすべての行を含むホールドスペースに置き換えられますp
パターンスペースを標準出力にコピーしますできた!