在 bash 中,如何在一个文件中找到与另一个文件的任何行都不匹配的模式?

in bash, how can I find a pattern in one file that doesn't match any line of another file?

如何在一个文件中找到与另一个文件的任何行都不匹配的模式

我知道 grep 有一个 -f 选项,所以我可以给它一个模式文件,而不是给 grep 一个模式。

(a.a 是我的主文件)

user@system:~/test# cat a.a
Were Alexander-ZBn1gozZoEM.mp4
Will Ate-vP-2ahd8pHY.mp4

(p.p 是我的模式文件)

user@system:~/test# cat p.p
ZBn1gozZoEM
0maL4cQ8zuU
vP-2ahd8pHY

所以命令可能类似于

somekindofgrep p.p a.a

但它应该给出 0maL4cQ8zuU,它是模式文件中的模式,p.p,与文件 a.a

中的任何内容都不匹配

我不确定要执行什么命令。

$grep -f p.p a.a<ENTER>
Were Alexander-ZBn1gozZoEM.mp4
Will Ate-vP-2ahd8pHY.mp4
$

我知道如果 a.a 中有一行与 p.p 中的任何模式都不匹配,那么 grep -f p.p a.a 将不会显示它。如果我做 grep -v -f p.p a.a 那么它只会显示 a.a 的那一行,在 p.p

中不匹配

但我想知道(我的模式文件)p.p 中的模式与 a.a 不匹配!

我查看了 Make grep print missing queries,但他想要两个文件中的所有内容。而且,那里的答案之一提到了 -v 但我不太明白它适用于我的案例,因为 -v 显示了与任何模式都不匹配的文件行。所以有或没有 -v 对我没有帮助,因为我正在寻找一个与文件的任何行都不匹配的模式。

自制脚本:

#!/bin/bash

if [[ $# -eq 2 ]]
then
    patterns=""
    mainfile=""

    if [[ ! -f "$patterns" ]]
    then
        echo "ERROR: file $patterns does not exist."
        exit 1
    fi
    if [[ ! -f "$mainfile" ]]
    then
        echo "ERROR: file $mainfile does not exist."
        exit 1
    fi
else
    echo "Usage: [=10=] <PATTERNS FILE> <MAIN FILE>"
    exit 1
fi

while IFS= read -r pattern
do
    if [[ ! grep -q "$pattern" "$mainfile" ]]
    then
        echo "$pattern"
    fi
done < "$patterns"

如 user1934428 所建议的那样,此脚本循环处理文件 p.p 中的模式并打印出文件 a.a.

中未找到的任何模式

建议 awk 扫描 a.a 一次的脚本:

script.awk

FNR==NR{wordsArr[[=10=]] = 1; next} # read patterns list from 1st file into array wordsArr
{ # for each line in 2nd file
  for (i in wordsArr){ # iterate over all patterns in array
    if ([=10=] ~ i) delete wordsArr[i]; # if pattern is matched to current line remove the pattern from array
  }
}
END {for (i in wordsArr) print "Unmatched: " i} # print all patterns left in wordsArray

运行: script.awk

awk -f script.awk p.p a.a

测试:

p.p

aa
bb
cc
dd
ee

a.a

ddd
eee
ggg
fff
aaa

测试:

awk -f script.awk p.p a.a
Unmatched: bb
Unmatched: cc
# grep p.p pattern in a.a and output pattern 
# if grep is true (pattern matched in a.a)
xargs -i sh -c 'grep -q "{}" a.a && echo "{}"' < p.p
# if grep is false (pattern NOT matched in a.a <--- what you need)
xargs -i sh -c 'grep -q "{}" a.a || echo "{}"' < p.p

这是一个可能的解决方案,它基于对您正在尝试执行的操作的一种可能解释(full-string 匹配 p.p 中的行与第一个 [=12] 之间的子字符串=] 和 a.a 行中的最后一个 .):

$ awk '
    NR==FNR {
        sub(/[^-]*-/,"")
        sub(/\.[^.]*$/,"")
        file1[[=10=]]
        next
    }
    !([=10=] in file1)
' a.a p.p
0maL4cQ8zuU

以上内容将在每个 Unix 机器上使用任何 shell 中的任何 awk 稳健、可移植且高效地工作。它会比当前的 shell 循环答案快 运行 个数量级,比现有的 awk 答案或 xargs 答案快,并且无论哪个文件中存在哪个字符都可以工作,包括正则表达式元字符, p.p 中的搜索字符串是否作为子字符串或在 a.a 的其他上下文中存在。无论输入文件中有什么,它也将具有零安全问题。