web-dev-qa-db-ja.com

次の行に文字列が含まれていないgrep文字列

ディレクトリとそのサブディレクトリ内のすべてのファイルで特定の文字列を含む行を検索したいのですが、その直後の行に別の特定の文字列を含む結果を除外したいと思います。

たとえば、これは次のとおりです。

foo1 searchString bar
foo1 excludeString bar

foo2 searchString bar
something else

foo3 searchString bar

foo3 excludeString bar

foo4 searchString bar

これを返す必要があります:

foo2 searchString bar
foo3 searchString bar
foo4 searchString bar

そんなこと知ってる -Aは複数行を出力し、それは-v結果を除外します。しかし、私の現在のアプローチはgrep -r -A 1 "searchString" | grep -v "excludeString"明らかに機能しません。

2番目のgrepに、一致するものが見つかった場合は前の行も削除する必要があることを伝える方法はありますか?または、他の方法でこれを達成する方法はありますか?

パフォーマンスは私の主な関心事ではありません。ただし、コマンドが比較的覚えやすい場合は便利です。

5
tim

perl compatible regular expressions grepを使用できます。

$ pcregrep -M '(searchString.*\n)(?!.*excludeString)' file
foo2 searchString bar
foo3 searchString bar
foo4 searchString bar

searchStringの後に任意のchar.を検索し、0回以上*を繰り返し、その後に改行\nのみが続く場合は- not?!)パターン.*excludeStringその隣。複数行に一致させるために、オプション-Mがあります。

8
jimmij

sedの場合:

sed '/searchString/!d;$!N;/\n.*excludeString/!P;D' infile

使い方:

  • /searchString/!dは、searchStringと一致しない場合はその行を削除し、新しい行を読み込み、コマンドサイクルを最初からやり直します(つまり、残りのコマンドは実行されなくなります)。
  • 行がsearchStringと一致する場合、sed$!N;/\n.*excludeString/!P;Dを実行します [〜#〜] here [〜#〜] どのように機能するか;違いは、ここでは、\newline文字の後にパターンexcludeStringを探しているため、searchStringexcludeStringの両方に一致する行が出力されることです。 excludeStringに一致する行が続かない; searchStringexcludeStringの両方に一致する行がない場合(つまり、既知の入力)、\n.*の部分を削除して実行できます。
    sed '/searchString/!d;$!N;/excludeString/!P;D' infile
6
don_crissti