这不一定需要修改grep,尽管通过这样的修改您可能会获得更准确的进度条。
如果您通过一次 grep 调用来搜索“数千个文件”,则很可能您正在使用 -r 选项来递归目录结构。在这种情况下,甚至不清楚grep 知道它将检查多少个文件,因为我相信它会在探索整个目录结构之前开始检查文件。首先探索目录结构可能会增加总扫描时间(事实上,生成进度报告总是有成本的,这就是为什么很少有传统的 Unix 实用程序这样做的原因。)
在任何情况下,通过构建要扫描的文件的完整列表,然后将它们分批提供给grep,可能会获得一个简单但稍微不准确的进度条,可能是 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 指定四个并行进程。我刚刚从空中提取了这些数字;您可能想要调整它们。 )