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

Bob*_*Bob 8 bash grep

有时候我是grep成千上万的文件,看到某种进展(条形或状态)会很高兴.

我知道这不是一件容易的事,因为grep将搜索结果输出STDOUT,我的默认工作流是我将结果输出到文件,并希望将进度条/状态输出到STDOUTSTDERR.

这需要修改源代码grep吗?

理想的命令是:

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

和进展:

[curr file being searched], number x/total number of files
Run Code Online (Sandbox Code Playgroud)

写入STDOUTSTDERR

ric*_*ici 11

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

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

在任何情况下,一个简单但不太精确的进度条可以通过构建要扫描的文件的完整列表,然后将其输送到获得grep基于该批次的总规模在某些尺寸的批次,可能有100人,或可能.小批量将允许进行更精确的进度报告,但他们也将增加开销,因为他们需要额外的grep进程的启动,以及进程启动时间可能比grepping一个小文件的更多.将针对每批文件更新进度报告,因此您需要选择批量大小,以便定期更新,而不会增加过多的开销.将批处理大小基于文件的总大小(例如,使用stat以获取文件大小)将使进度报告更精确,但会增加处理启动的额外成本.

这种策略的一个优点是你也可以并行运行两个或多个greps,这可能会加快这个过程.


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

# 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
Run Code Online (Sandbox Code Playgroud)

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

您可能希望使用某些控制台代码使进度报告更清晰.以上只是为了让你入门.

将其划分为不同进程的最简单方法是将列表划分为X个不同的段,并为循环运行X,每个循环都有不同的起点.但是,它们可能不会同时完成,因此不是最佳的.更好的解决方案是GNU并行.你可能会这样做:

find . -type f -print0 |
parallel --progress -L 100 -m -j 4 grep -e "$pattern" > results.txt
Run Code Online (Sandbox Code Playgroud)

(这里-L 100指定每个grep实例最多应该给出100个文件,并-j 4指定四个并行进程.我只是将这些数字拉出来;你可能想要调整它们.)