bash - grep - 如何输出进度条或状态

标签 bash grep

有时我正在grep-ing 数以千计的文件,很高兴看到某种进度(栏或状态)。

我知道这不是微不足道的,因为 grep 将搜索结果输出到 STDOUT 并且我的默认工作流程是我将结果输出到一个文件并且想要进度条/status 输出到 STDOUTSTDERR

这是否需要修改 grep 的源代码?

理想的命令是:

grep -e "STRING"--results="FILE.txt"

和进展:

[curr file being searched], number x/total number of files

写入STDOUTSTDERR

最佳答案

这不一定需要修改 grep,尽管通过这样的修改您可能会获得更准确的进度条。

如果您通过一次 grep 调用 grep 搜索“数千个文件”,很可能您正在使用 -r 选项来递归目录结构。在那种情况下,甚至不清楚 grep 是否知道它将检查多少个文件,因为我相信它会在探索整个目录结构之前开始检查文件。首先探索目录结构可能会增加总扫描时间(事实上,生成进度报告总是有成本的,这就是为什么很少有传统的 Unix 实用程序这样做的原因。)

在任何情况下,通过构建要扫描的文件的完整列表,然后将它们以一定大小(可能是 100,或可能基于批处理的总大小。小批量将允许更准确的进度报告,但它们也会增加开销,因为它们需要额外的 grep 进程启动,并且进程启动时间可能比 grep 小文件长。进度报告将针对每批文件进行更新,因此您需要选择一个批大小,以便在不增加太多开销的情况下定期更新。将批处理大小基于文件的总大小(例如,使用 stat 获取文件大小)会使进度报告更加准确,但会增加流程启动的额外成本。

此策略的一个优点是您还可以并行运行两个或多个 grep,这可能会稍微加快处理速度。


从广义上讲,一个简单的脚本(它只是按数量而不是大小划分文件,并且不尝试并行化)。

# Requires bash 4 and Gnu grep
shopt -s globstar
files=(**)
total=${#files[@]}
for ((i=0; i<total; i+=100)); do
  echo $i/$total >>/dev/stderr
  grep -d skip -e "$pattern" "${files[@]:i:100}" >>results.txt
done

为简单起见,我使用 globstar (**) 将所有文件安全地放入一个数组中。如果您的 bash 版本太旧,那么您可以通过遍历 find 的输出来完成它,但是如果您有很多文件,那效率就不是很高。不幸的是,据我所知,没有办法编写只匹配文件的 globstar 表达式。 (**/ 只匹配目录。)幸运的是,GNU grep 提供了 -d skip 选项,它可以静默地跳过目录。这意味着文件计数会稍微不准确,因为目录会被计算在内,但这可能不会有太大区别。

您可能希望使用一些控制台代码使进度报告更清晰。以上内容只是为了帮助您入门。

将其分成不同进程的最简单方法是将列表分成 X 个不同的部分并运行 X 个不同的 for 循环,每个循环都有不同的起点。然而,它们可能不会同时完成,所以这是次优的。更好的解决方案是 GNU 并行。你可能会这样做:

find . -type f -print0 |
parallel --progress -L 100 -m -j 4 grep -e "$pattern" > results.txt

(这里 -L 100 指定最多应该给每个 grep 实例 100 个文件,而 -j 4 指定四个并行进程。我只是提取了这些数字空气中;您可能需要调整它们。)

关于bash - grep - 如何输出进度条或状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37683758/

相关文章:

unix - 如何从 grep 中保存匹配和不匹配的内容

shell - 如何从另一个文件中查找包含任何字符串的行?

linux - 仅使用正则表达式查找一行中的第一个匹配项

linux - Linux 中的 fork() 和 grep 是什么意思?

linux - grep 换行后的内容

linux - 禁用通配符扩展到 Bash 脚本不起作用

Bash 脚本 - 在脚本中执行和 grep 命令

node.js - 将环境变量传递给进程

python - 如何在 python 中更快地拆分列?

bash - 使用 Bash 确定 URL 是 HTTP 还是 HTTPS