使用 grep,如何匹配一個模式並反轉匹配另一個模式?

使用 grep,如何匹配一個模式並反轉匹配另一個模式?

使用grep,我想選擇與某個模式匹配且與另一個模式不匹配的所有行。我希望能夠使用一次調用,grep以便我可以使用--after-context選項(或--before-context、 或--context)。

-v在這裡不可行,因為它否定了我傳遞給grep使用該-e選項的所有模式。

例子

我想尋找匹配的行needle,忽略匹配的行ignore me,並帶有一行以下上下文。

這是我的輸入檔:

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-regex標誌來啟動 PCRE,然後提供否定的先行斷言,範例如下

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

這裡要注意的主要事情是(?:(?!STRING).)*is to STRINGas [^CHAR]*is toCHAR

答案3

我建議使用 awk 來代替,因為它可以更好地處理多行 IO。任何一個1)將結果透過管道傳輸到 GNU awk--\n作為記錄分隔符,或者2)在 awk 中進行所有配對。

選項1

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

輸出:

four needle                                                                                  
five
--

請注意,此選項在整個記錄中搜尋ignore me、設定FS=1並匹配$1以僅與第一行進行比較。

選項2

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

相關內容