nim*_*mai 5 performance grep search
我有一个字符串列表,对于每个字符串,我想检查它是否出现在一个大的源代码目录中。
我来到了一个 GNU grep 解决方案,它给了我我想要的:
for key in $(cat /tmp/listOfKeys.txt); do
if [ "$(grep -rio -m 1 "$key" . | wc -l)" = "0" ]; then
echo "$key has no occurence";
fi
done
Run Code Online (Sandbox Code Playgroud)
但是,它根本没有效率,因为它总是 grep 目录中的每个文件,即使它很早就找到了匹配项。由于要查找的键很多,而且要搜索的文件也很多,因此无法按原样使用。
您知道一种使用“标准”unix 工具有效执行此操作的方法吗?
至少可以简化为:
set -f # needed if you're using the split+glob operator and don't want the
# glob part
for key in $(cat /tmp/listOfKeys.txt); do
grep -riFqe "$key" . ||
printf '%s\n' "$key has no occurrence"
done
Run Code Online (Sandbox Code Playgroud)
这将在第一次出现后停止搜索,key
并且不会将键视为正则表达式(或可能的选项grep
)。
为了避免多次读取文件,并假设您的键列表是每行一个键(与上面代码中分隔的空格/制表符/换行符相反),您可以使用 GNU 工具:
find . -type f -size +0 -printf '%p\0' | awk '
ARGIND == 2 {ARGV[ARGC++] = $0; next}
ARGIND == 4 {a[tolower($0)]; n++; next}
{
l = tolower($0)
for (i in a) if (index(l, i)) {
delete a[i]
if (!--n) exit
}
}
END {
for (i in a) print i, "has no occurrence"
}' RS='\0' - RS='\n' /tmp/listOfKeys.txt
Run Code Online (Sandbox Code Playgroud)
它经过优化,一旦看到它就会停止寻找,key
一旦找到所有密钥就会停止并且只会读取文件一次。
它假设键在listOfKeys.txt
. 它将以小写形式输出键。
上面的 GNUisms 是-printf '%p\0'
,ARGIND
以及awk
处理 NUL 分隔记录的能力。前两个可以通过以下方式解决:
find . -type f -size +0 -exec printf '%s\0' {} + | awk '
step == 1 {ARGV[ARGC++] = $0; next}
step == 2 {a[tolower($0)]; n++; next}
{
l = tolower($0)
for (i in a) if (index(l, i)) {
delete a[i]
if (!--n) exit
}
}
END {
for (i in a) print i, "has no occurrence"
}' step=1 RS='\0' - step=2 RS='\n' /tmp/listOfKeys.txt step=3
Run Code Online (Sandbox Code Playgroud)
第三个可以用像这样的技巧来解决,但这可能不值得付出努力。请参阅Barefoot IO 的解决方案以了解完全绕过该问题的方法。
GNU grep(以及我知道的大多数变体)提供了一个-f
选项,它完全符合您的需要。该fgrep
变体将输入行视为普通字符串而不是正则表达式。
fgrep -rio -f /tmp/listOfKeys.txt .
Run Code Online (Sandbox Code Playgroud)
如果您只想测试是否找到至少一个匹配项,请添加-q
选项。根据 Stéphane 的评论,如果您需要知道未找到哪些字符串,请添加该-h
选项,然后通过这个常见的 awk 习语进行管道传输:
fgrep -h -rio -f /tmp/listOfKeys.txt . |
awk '{$0=tolower($0)}; !seen[$0]++' |
fgrep -v -i -x -f - /tmp/listOfKeys.txt
Run Code Online (Sandbox Code Playgroud)
第二个fgrep
现在使用第一个fgrep
的输出(不区分大小写的唯一性),反转意义,并显示来自密钥文件的非匹配字符串。