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

时间:2016-06-07 15:40:52

标签: bash grep

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

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

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

理想的命令是:

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

和进展:

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

写入 STDOUT STDERR

5 个答案:

答案 0 :(得分:8)

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

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

在任何情况下,可以通过构建要扫描的文件的完整列表然后将它们以一定大小(可能是100)或可能基于的批量提供给grep来获得简单但稍微不准确的进度条。批次的总大小。小批量将允许更准确的进度报告,但是它们也会增加开销,因为它们需要额外的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

为简单起见,我使用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

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

答案 1 :(得分:1)

我正常使用这样的东西:

grep | tee "FILE.txt" | cat -n | sed 's/^/match: /;s/$/     /' | tr '\n' '\r' 1>&2

它并不完美,因为它只显示匹配,如果它们长或差异很大,则会有错误,但它应该为您提供一般的想法。

或简单的点:

grep | tee "FILE.txt" | sed 's/.*//' | tr '\n' '.' 1>&2

答案 2 :(得分:1)

尝试并行程序

find * -name \*.[ch] | parallel -j5 --bar  '(grep grep-string {})' > output-file

虽然我发现这比简单的

find * -name \*.[ch] | xargs grep grep-string > output-file

答案 3 :(得分:0)

我非常确定您需要更改grep源代码。而这些变化将是巨大的。

目前grep在完成解析整个文件之前不知道文件的行数。根据您的要求,它需要解析文件2次或至少以其他方式确定整行数。

第一次确定进度条的行数。第二次实际工作是搜索你的模式。

这不仅会增加运行时间,还会违反主要的UNIX哲学之一。

  
      
  1. 让每个程序做好一件事。要做一份新工作,重新构建,而不是通过添加新的&#34;功能来复杂旧程序#34;。 (source
  2.   

可能还有其他工具可供您使用,但afaik grep不适合这里。

答案 4 :(得分:0)

此命令显示进度(速度和偏移),但不显示总量。但是,可以手动估算。

dd if=/input/file bs=1c skip=<offset> | pv | grep -aob "<string>"