grep
/ egrep
を誤用しているようです。
複数行の文字列を検索しようとしましたが、探しているものが一致するはずだとわかっているのに一致するものが見つかりませんでした。元々、正規表現が間違っていると思っていましたが、最終的にはこれらを読みました。ツールは行ごとに動作します(また、私の正規表現は非常に些細なもので、問題にはなりませんでした)。
では、複数行のパターンを検索するためにどのツールを使用しますか?
コメント
回答
ここに “sa sed
複数行にわたるgrep
のような動作を提供するもの:
sed -n "/foo/{:start /bar/!{N;b start};/your_regex/p}" your_file
仕組み
-
-n
は、すべての行を印刷するデフォルトの動作を抑制します -
/foo/{}
はfoo
を実行し、波線の内側にあるものを一致する行に移動します。foo
をパターンの開始部分に置き換えます。 -
:start
は、正規表現の終わりが見つかるまでループを続けるのに役立つ分岐ラベルです。 -
/bar/!{}
は、波線の内容を実行します。bar
と一致しない行。とパターンの終了部分。 -
N
は、アクティブなバッファに次の行を追加します(sed
はこれをパターンスペースと呼びます) -
b start
は、作成したstart
ラベルに無条件に分岐しますパターンスペースにbar
が含まれていない限り次の行を追加し続けるために以前に。 -
/your_regex/p
your_regex
と一致する場合、パターンスペースを出力します。your_regex
は、複数の行で照合する式全体に置き換える必要があります。
コメント
- +1これをtooliktに追加します!ありがとう。
- 注:MacOSでは、これにより
sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
-
sed: unterminated {
エラーが発生します - @Nomaedショットは暗闇の中でここにありますが、正規表現に” {“の文字が含まれていますか?その場合、’バックスラッシュでエスケープする必要があります。
- @Nomaed
sed
実装間の違い。その回答の推奨事項に従って上記のスクリプトを標準に準拠させようとしましたが、” start “は未定義であると言われましたラベル。そのため、’これを標準に準拠した方法で実行できるかどうかわかりません。管理している場合は、自由に回答を編集してください。
回答
私は通常ツールを使用します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
コメント
- この
sed
の提案はスキップしますdef
が見つかる行
回答
単に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修飾子と呼ばれ、正規表現のドットを文字だけでなく改行にも一致させます。
コメント
- このソリューションを試してみると、出力が’ def
ただし、ファイルの最後に移動します’ blah ‘
-P
オプションをサポートしていませんsed
の提案を試しましたしかし、’代替のgrepのインストールまでは行きませんでした。$ grep --version
:iv id = “bbd9ce5411” Windows Git Bash の> にはオプション-P, --perl-regexp
がありますが、(?s)
にはありません’そこでは機能していないようです。それでも最初の行のみが表示されます。同じテスト文字列を使用した同じパターンは、 regex101.com で機能します。 Git Bashに代替手段はありますか? sed
? (sed (GNU sed) 4.8
ここ)回答
ここで「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("",<>);
:これにより、ファイル全体が読み取られ、その内容(改行とすべて)が変数
。次に、foo\nbar.*\n
との一致を試み、一致する場合は出力します(特別な変数$&
は、最後に見つかった一致を保持します)。 ///m
は、改行間で正規表現を一致させるために必要です。
-0
は、入力レコードの区切り文字を設定します。これを00
に設定すると、「段落モード」がアクティブになり、Perlはレコード区切り文字として連続する改行(\n\n
)を使用します。連続する改行がない場合、ファイル全体が一度に読み取られます(スラップされます)。
###警告:大きなファイルに対してはこれを行わないでください 。ロードされます。ファイル全体がメモリに格納されるため、問題が発生する可能性があります。
コメント
- 私は’ t Perlについてはよく知っていますが、厳密に言えば、’は
my $f=join("",<>);
である必要はありませんか? - @Sapphire_Brickのみ厳密モードの場合(
use strict;
)。 ‘特に大きなスクリプトを書くときは、入るのが良い習慣ですが、’このような小さなワンライナーにはやり過ぎです1つ。
回答
ファイルがあるとします。 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の代替 sift は、複数行のマッチングをサポートしています(免責事項:私は作成者です)。
testfile
に含まれるもの:
<book> <title>Lorem Ipsum</title> <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore 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: labore et dolore magna aliqua</description>
sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename
(抽出して説明を再フォーマットします)
結果:
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"
コメント
- とても素敵なツールです。おめでとう! Ubuntuのようなディストリビューションに含めてみてください。
回答
grepを使用してこれを解決しました-別のgrepを持つオプション。
grep first_line_word -A 1 testfile | grep second_line_word
-A 1オプションは、見つかった行の後に1行を出力します。もちろん、それはあなたのファイルと単語の組み合わせに依存します。しかし、私にとっては、これが最速で信頼性の高いソリューションでした。
コメント
- alias grepp = ‘ grep –color = auto -B10 -A20 -i ‘ then cat somefile | grepp blah | grepp foo | grepp bar …はい、これらの-Aと-Bは非常に便利です…最良の答えがあります
- これは’ tの超決定論的ではなく、パターン全体を無視して、(近接性に基づいて)別の単一行を取得するだけです。最初の行に)。 ‘ある種のパターンに到達するために、プログラムがどこまで進む必要があるかをプログラムに伝える方がよい’ ‘一致させようとしているテキストの終わりは間違いありません。たとえば、
testfile
が更新されてsecond_line_word
が3行目にある場合、最初の行が欠落しているだけではありません( 2番目のgrep
)ですが、’ 2つの間に表示され始めた行を見逃していません。 - これアドホックコマンドには十分なMOであり、すでに理解している出力に1行だけが必要です。 ‘ ‘がOPの目的であるとは思いませんが、おそらくその時点でコピー/貼り付けすることもできます。アドホックです。
回答
これを行う1つの方法は、Perlを使用することです。例えばfoo
という名前のファイルの内容は次のとおりです:
foo line 1 bar line 2 foo foo foo line 5 foo bar line 6
これで、Perlが表示されます。 fooで始まり、その後にbarで始まる行が続くすべての行と一致します:
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
- 変数
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
コメント
- Perlをより慣用的なものに短縮できると言って立ち寄っただけです:
perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
回答
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
は、パターンに「bar」が含まれていない場合、ラベル「gotoloop」を定義します -
/bar/!{
-
h
ホールドスペースをパターンに置き換えると、「ここ」がホールドスペースに保存されます -
b loop
ラベル「loop」への分岐 -
:loop
はラベル「loop」を定義します -
N
はパターンをホールドスペースに追加します。
ホールドスペースには次のものが含まれます:
“here”
“is the” -
:gotoloop
これでステップ4になり、行に「bar」が含まれるまでループします。 -
/bar/
ループが終了し、「bar」が見つかりました。」 sパターンスペース - パターンスペースは、メインループ中に保存された「foo」と「bar」の間のすべての行を含むホールドスペースに置き換えられます
-
p
パターンスペースを標準出力にコピー
完了!
コメント
- よくできました、+ 1。私は通常、改行をSOHにtr ‘し、通常のsedコマンドを実行してから改行を置き換えることにより、これらのコマンドの使用を避けます。
grep
でこれを行う方法を尋ねています。これらは密接に関連していますが、重複ではありません、IMO。"grep"
と言って動詞”をgrep “に提案し、上位の回答を承認済みを含め、’ grepを使用しないでください。