前言
首先,您的命令有几个缺陷会导致它在某些情况下失败:
它不适用于由非 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 并继续输入下一行,可能会丢失当前行中的其他单词。
并行化
根据您的内核数量,您还可以通过并行化 greps 来实现良好的加速:
( 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 倍。