我使用以下linux shell命令来计算大型二进制文件中的单词数,这需要花费太多时间(〜10秒);
strings /path/to/<binary_file> | grep -c -E "word1|word2|...|wordN"
如何加快这一过程?
我尝试仅使用grep
命令,但找不到某些单词,因此必须使用strings
。我尝试添加wc
而不是-c
,但这比较慢。
顺便说一句,我在Android环境中没有并行命令。
实际上,我仍然无法编写与此组合等效的C语言,我们将不胜感激。
答案 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
在这里,我假设最密集的处理工作是由strings
和grep
完成的,并且由于过滤,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倍。