我有一个包含约 1M 个文件的目录,需要搜索特定模式。我知道如何对所有文件执行此操作:
find /path/ -exec grep -H -m 1 'pattern' \{\} \;
不需要完整的输出(太慢)。几次第一次点击都可以,所以我尝试限制行数:
find /path/ -exec grep -H -m 1 'pattern' \{\} \; | head -n 5
这会产生 5 行,然后是
find: `grep' terminated by signal 13
并find
继续工作。这很好解释这里。我尝试了quit
行动:
find /path/ -exec grep -H -m 1 'pattern' \{\} \; -quit
这仅输出第一个匹配项。
是否可以将 find 输出限制为特定数量的结果(例如提供一个quit
类似于 simple to 的参数head -n
)?
答案1
由于您已经在使用 GNU 扩展(-quit
, -H
, -m1
),您不妨使用 GNUgrep
的-r
选项,因此--line-buffered
它会在找到匹配项后立即输出匹配项,因此一旦找到匹配项,它更有可能被 SIGPIPE 杀死。它写了第六行:
grep -rHm1 --line-buffered pattern /path | head -n 5
对于find
,您可能需要执行以下操作:
find /path -type f -exec sh -c '
grep -Hm1 --line-buffered pattern "$@"
[ "$(kill -l "$?")" = PIPE ] && kill -s PIPE "$PPID"
' sh {} + | head -n 5
也就是说,换行grep
(sh
您仍然希望运行grep
尽可能少的调用,因此是{} +
),并在因 SIGPIPE 死亡时sh
杀死其父级 ( find
) 。grep
另一种方法可能是使用xargs
作为-exec {} +
.xargs
当它生成的命令因信号而死亡时立即退出,因此:
find . -type f -print0 |
xargs -r0 grep -Hm1 --line-buffered pattern |
head -n 5
(-r
并且-0
是 GNU 扩展)。一旦grep
写入损坏的管道,grep
和xargs
都会退出,find
并且下次打印内容时也会退出本身。跑find
下去stdbuf -oL
可能会让这件事发生得更快。
POSIX 版本可能是:
trap - PIPE # restore default SIGPIPE handler in case it was disabled
RE=pattern find /path -type f -exec sh -c '
for file do
awk '\''
$0 ~ ENVIRON["RE"] {
print FILENAME ": " $0
exit
}'\'' < "$file"
if [ "$(kill -l "$?")" = PIPE ]; then
kill -s PIPE "$PPID"
exit
fi
done' sh {} + | head -n 5
效率非常低,因为它为每个文件运行多个命令。
答案2
避免错误的解决方案可能是这样的:
find / -type f -print0 \
| xargs -0 -L 1 grep -H -m 1 --line-buffered 2>/dev/null \
| head -10
在此示例中,一旦命令失败,xargs 将停止,因此只会出现一个管道错误,该错误将被 stderr 重定向过滤。
答案3
您grep
一次一个文件。使用您的-quit
,您可以在第一个成功的 grep 处停止查找。
[更新] 我的第一个解决方案是一次 grep 多个文件:
find /path/ -type f -exec grep -H -m 1 'pattern' \{\} + -quit | head -n 5
(神奇之处在于子命令+
末尾的。添加了 。如果您确定 /path/ 包含多个文件,您可能需要删除该选项)-exec
-type f
-H
grep
正如 @StéphaneChazelas 所报告的,这里的问题是该-exec
命令是异步执行的,并且总是返回true
=>find
在第一个文件处退出。
如果我们想在完成find
时停止,还必须接收到正在接收的SIGPIPE (信号13)。这意味着必须通过管道发送一些东西。head
find
grep
find
这是一个快速而肮脏的技巧,并根据 Stéphane 的建议进行了增强:
find /path/ -type f -exec grep -H -m 1 --line-buffered 'pattern' {} + -printf '\r' | head -n 5
随着-printf '\r'
我强制find
输出一个无害的字符,它(希望)不会改变 的输出grep
。一旦head
停止,find
将收到 SIGPIPE 并停止。
[update2] 我警告过你这是一个肮脏的黑客行为。这是一个更好的解决方案:
find /path/ -type f -exec grep --quiet 'pattern' {} ";" -print | head -n 5
在这里,这不再是grep
打印文件名,而是find
=> 不再有“grep 由信号 13 终止”并find
以head
.问题是 不再打印匹配的行grep
。
[update3] 最后,按照@Andrey的建议,下面的无耻丑陋命令将解决最后一个问题:
find /path/ -type f \
-exec grep --quiet 'pattern' {} \; \
-printf '%p:' \
-exec grep -h -m 1 'pattern' {} \; \
| head -n 5`
答案4
对于更简单的情况,替代路线可能只是一个此处字符串而不是管道。例如-
find . -exec stat -c %y {} \; | head -n1
会看到与上面相同的问题。一种可以考虑的简单方法 -
head -n1 <<<$(find . -exec stat -c %y {} \;)