grep を使用して、パターンを一致させ、別のパターンを反転一致させるにはどうすればよいですか?

grep を使用して、パターンを一致させ、別のパターンを反転一致させるにはどうすればよいですか?

ではgrep、パターンに一致し、別のパターンには一致しないすべての行を選択します。 を 1 回呼び出すだけで、オプション (または、または)grepを使用できるようにしたいと考えています。--after-context--before-context--context

-vgrepこの-eオプションを使用すると、渡すすべてのパターンが否定されるため、ここでは実行できません。

needle一致する行を無視して、ignore me次のコンテキストの 1 行を含む、一致する行を検索します。

入力ファイルは次のとおりです:

one needle ignore me
two
three
four needle
five

必要な出力は次のとおりです。

four needle
five

ご覧のとおり、この単純な解決策は機能しません。

$ cat file | grep --after-context=1 needle | grep -v 'ignore me'
two
---
four needle
five

答え1

GNU grepをお持ちの場合は、Perl 正規表現、これは否定構文

grep -A1 -P '^(?!.*ignore me).*needle'

GNU grepをお持ちでない場合は、awkの前後のコンテキストオプションをエミュレートする

awk -v after=3 -v before=2 '
/needle/ && !/ignore me/ {
    for (i in h) {
        print h[i];
        delete h[i];
    }
    until = NR + after;
}
{
    if (NR <= until) print $0; else h[NR] = $0;
    delete h[NR-before];
}
END {exit !until}
'

答え2

GNUを使用しているようですGNU grepでは、フラグを渡して--perl-regexPCREをアクティブにし、否定先読みアサーションを提供することができます。以下に例を示します。

grep --after-context=1 \
--perl-regex '^(?:(?!ignore me).)*needle(?:(?!ignore me).)*$' file.txt
four needle
five

ここで注目すべき点(?:(?!STRING).)*は、STRING[^CHAR]*CHAR

答え3

複数行のIOをより適切に処理できるawkを使用することをお勧めします。1)結果を--\nレコードセパレータとしてGNU awkにパイプするか、2)すべてのマッチングを awk で実行します。

オプション1

<file grep -A1 needle | awk '!/ignore me/' RS='--\n' ORS='--\n'

出力:

four needle                                                                                  
five
--

注意: このオプションは、レコード全体でignore me、 set FS=1、 match against を検索し$1、最初の行とのみ比較します。

オプション2

<file awk 'a-- > 0; $0 ~ re1 && $0 !~ re2 { print $0; a=after }' re1=needle re2='ignore me' after=1

関連情報