最快的字符串二进制文件| grep

时间:2018-07-06 22:29:18

标签: android linux shell grep sh

我使用以下linux shell命令来计算大型二进制文件中的单词数,这需要花费太多时间(〜10秒);

strings /path/to/<binary_file> | grep -c -E "word1|word2|...|wordN"

如何加快这一过程?

我尝试仅使用grep命令,但找不到某些单词,因此必须使用strings。我尝试添加wc而不是-c,但这比较慢。 顺便说一句,我在Android环境中没有并行命令。

实际上,我仍然无法编写与此组合等效的C语言,我们将不胜感激。

1 个答案:

答案 0 :(得分:1)

前言

首先,您的命令存在一些缺陷,在某些情况下会使其失败:

  • 它不适用于由非ASCII字符(如重音字母)组成的单词,因为它们由strings过滤。不过,您可能不需要这些单词。

  • 如果孤立单词,它将丢失少于4个字符的单词。您应该使用strings -n1作为通用解决方案。

  • 当多个单词属于同一行文本时,它将丢失单词,因为grep -c会计算行而不是单词。

发布了Android的grep(从Android 8.1开始)的问题:

  • 在Android上,您必须使用grep -E 'word1|word2|...|wordN'而不是grep -F -e word1 -e word2 .. -e wordN,这是等效的,但通常速度要快得多。这是因为Android 8中存在一个错误,导致该错误无法正确计数。

  • 在Android上,我不仅会使用grep -a,还会使用grep -za。在Linux上,GNU grep将二进制文件中的NUL(0)字符视为行尾,并且-z选项不仅无用,而且也不可取,因为输出行也将以NUL而不是换行符终止。但是Android版本的行为有所不同:NUL字符需要明确地视为换行符,否则将忽略以下内容;偶然情况下,仍然会使用传统的换行符来输出行。

限制strings

的输出

通过将-n选项设置为strings到所要查找的最小单词的大小,您可能会略微提高速度。例如,如果您要查找的单词都不少于7个字符,请使用strings -n7。因此,您将减少进程间的通信,并且grep不会打扰显然不适合该模式的搜索行。

摆脱strings

strings有点贵,可能没有什么好处(取决于所滤除的二进制字符数量-YMMV,请参阅下一节的评论),甚至有害(请参阅我的前言)。您可以通过以下方法摆脱它:

grep -F -a -o -e word1 -e word 2 ... -e wordN /path/to/binary_file \
| wc -l

由于Android grep的上述问题,因此是Android版本:

grep -z -a -o -E 'word1|word2|...|wordN' /path/to/binary_file \
| wc -l

请记住,使用grep | wc是强制性的,因为grep -c并不计算单词,而是行。这就是grep -c看起来更快的原因,因为一旦找到一个单词,grep就会计数+1并继续到下一个输入行,可能会丢失当前行中的其他单词。

并行化

根据您的内核数量,还可以通过并行化grep来提高速度:

( grep -F -a -o -e word1 -e word2 /path/to/binary_file &
  grep -F -a -o -e word3 -e word4 /path/to/binary_file
) | wc -l

由于Android grep的上述问题,因此是Android版本:

( grep -z -a -o -E 'word1|word2' /path/to/binary_file &
  grep -z -a -o -E 'word3|word4' /path/to/binary_file
) | wc -l

在这里,我假设最密集的处理工作是由stringsgrep完成的,并且由于过滤,wc的工作量很小。取决于搜索模式,情况可能并非如此。同样,如果strings在过滤二进制文件方面做得很好,则最好将其保留为第一条指令。 YMMV。

使用tr代替strings

strings可能会过滤掉许多不必要的(非ASCII)字符,这可以真正帮助grep处理更少的数据。通过过滤出不属于您要查找的单词的每个字符,您可以走得更远。例如,如果您查找“ word1”,“ word2”和“ word3”,则可以过滤掉所有不是w,o,r,d,1、2、3的字符。

如果您有权使用tr命令行工具,则可以使用它代替strings获得好处:

tr -c -s 'word123' '\n' < /path/to/binary_file \
| grep -F -o -e word1 -e word2 -e word3 \
| wc -l

由于Android grep的上述问题,因此是Android版本:

tr -c -s 'word123' '\n' < /path/to/binary_file \
| grep -E -o 'word1|word2|word3' \
| wc -l

(请注意,tr不能与非ASCII多字节字符一起使用,但是由于您在ASCII模式下使用strings,因此您不必在意这一点)

基准化

这是对24 MB声音文件进行的一些测试;该平台是我的Android 8.1八核手机。根据您的输入文件,搜索字符串和内核数,您显然会获得其他结果,但这将使您对可能的速度提高有所了解。

# Your original command (fixed)
$ time strings -n1 test | grep -E 'A|B|C|D' -o | wc -l
403380
    0m18.93s real     0m10.05s user     0m13.77s system

# grep alone
$ time grep -z -a -E 'A|B|C|D' -o test | wc -l
403380
    0m07.03s real     0m05.26s user     0m00.04s system

# Parallelized grep (x2)
$ time ( grep -z -a -E 'A|B' -o test &
         grep -z -a -E 'C|D' -o test
       ) | wc -l
403380
    0m03.56s real     0m03.12s user     0m00.03s system

# Parallelized grep -F (x4 - one per string to search)
$ time ( grep -z -a -F A -o test &
         grep -z -a -F B -o test &  
         grep -z -a -F C -o test &
         grep -z -a -F D -o test
       ) | wc -l 
403380
    0m01.04s real     0m01.88s user     0m00.05s system

# tr instead of string
$ time tr -c -s 'ABCD' '\n' < test | grep -E 'A|B|C|D' -o | wc -l
403380
    0m01.60s real     0m01.27s user     0m01.41s system

# Parallelized tr + grep (x2)
$ time ( tr -c -s 'AB' '\n' < test | grep -E 'A|B' -o &
         tr -c -s 'CD' '\n' < test | grep -E 'C|D' -o
       ) | wc -l
403380
    0m00.95s real     0m01.23s user     0m02.20s system

如您所见,在这些测试条件下,使用strings的版本和使用tr的最后一个版本之间的速度提高了约20倍。