【问题标题】:Fastest strings binary file | grep最快的字符串二进制文件 | grep
【发布时间】:2018-12-15 12:46:28
【问题描述】:

我使用以下 linux shell 命令来计算一个大二进制文件中的字数,这需要太多时间(~10 秒);

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

我怎样才能加快这个过程?

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

实际上我仍然无法编写这种组合的 C 等价物,任何帮助将不胜感激。

【问题讨论】:

  • 是不是因为你认为-c计算的是字数而不是行数而无法找到一些单词?
  • 这个组合可以正确找到所有二进制的单词,当我使用string打印单词时看到它。

标签: android linux shell grep sh


【解决方案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 并继续输入下一行,可能会丢失当前行中的其他单词。

并行化

根据您的内核数量,您还可以通过并行化 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

这里我假设最密集的处理是由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 倍。

【讨论】:

  • 我试了一下,你的建议找不到一些词,而且性能似乎更慢。 time strings /system/bin/app_process | grep -c -E "Android|android" 41 0m00.10s real 0m00.13s user 0m00.00s system time grep -F -a -o -E "Android|android" /system/bin/app_process | wc -l 43 0m00.11s real 0m00.16s user 0m00.02s system
  • 答案已更新。我从您的测试用例中看到的是您的命令遗漏了一些单词,而不是我的。顺便说一句,如果你想比较时间,为什么不尝试实际的二进制文件,10 秒不是那么长,结果会更相关。您是否对并行化的 grep 进行了速度测试?
  • 您好,我试过并行化的 grep 没问题,但 grep 本身缺少一些单词。例如字符串 | grep 找到 6440 个单词,但 grep 只找到 26 个。所以,如果您对并行字符串有任何建议 | grep 选项,我可以试试。关于 tr,不幸的是我无法让它运行,它给出了关于 tr 使用的错误。
  • @Phillip 你能仔细检查一下你使用的命令和我的完全一样吗?我已经在一个 100M 的纯随机内容文件上测试了所有这些,并且都给了我相同的结果(虽然在 Linux 上,而不是在 Android 上)。
  • 我刚刚用这些参数在 Oreo 上尝试了 tr 的玩具盒版本,它运行良好。不知道牛轧糖。您可能想尝试不使用 -s 选项,因为它只是一种优化。我还测试了我的命令,所有命令都运行良好,而你的 strings 是迄今为止最慢的。至于grep -c,我在回答中解释了为什么它比管道连接到 wc 更快:因为它漏掉了单词。使用不准确的计数方法有什么意义?
猜你喜欢
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 2011-02-28
  • 2016-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多