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

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

では、複数行のパターンを検索するためにどのツールを使用しますか?

コメント

  • sed、awk、またはgrepを使用した複数行のパターン一致の重複の可能性
  • @ CiroSantilli-このQとリンク先のQが重複しているとは思いませんが、もう1つのQは、’どのように複数行のパターンマッチングを行うか(つまり、どのツールを使用する必要があるか/できるか)を尋ねています。これを行うために使用します)これはgrepでこれを行う方法を尋ねています。これらは密接に関連していますが、重複ではありません、IMO。
  • @simこれらのケースは判断が難しい:あなたの主張がわかります。この特定のケースは、重複したbecauとして優れていると思いますユーザーが"grep"と言って動詞”をgrep “に提案し、上位の回答を承認済みを含め、’ grepを使用しないでください。
  • ここでは複数行の正規表現が必要であることを示すものはありません。入力データと期待される出力データを含む実際の例と、これまでの取り組みを示すことを検討してください。

回答

ここに “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 ‘

  • 多分あなたのgrepは-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ここ)
  • コンテキストを出力に追加する方法を知っていますか? grep-1は’ここでは機能しません。
  • 回答

    ここで「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 

    どのように機能しますか?ステップバイステップで作成

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

    完了!

    コメント

    • よくできました、+ 1。私は通常、改行をSOHにtr ‘し、通常のsedコマンドを実行してから改行を置き換えることにより、これらのコマンドの使用を避けます。

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です