原生bash中合理的高性能十六进制解码?

时间:2015-05-12 20:08:21

标签: bash

我有一个2MB的文件,这是一个由空格分隔的十六进制值序列。例如:

3F 41 56 00 00

在Bash中轻松做到这一点:

cat hex.txt | tr -s " " $'\n' | while read a; do 
    echo $a | xxd -r -p | tee -a ascii
done

f=$(cat hex.txt)
for a in $f; do
    echo $a | xxd -r -p | tee -a ascii
done

两者都非常缓慢。

我掀起了一个C程序,它在大约两秒钟内转换了文件,后来意识到我可以做到这一点:

cat hex.txt | xxd -r -p

由于我已经转换了文件并找到了最佳解决方案,我的问题不是关于转换过程本身,而是如何优化我的前两次尝试,好像第三次尝试不可能。有什么办法可以加快这些单线速度,或者Bash对于这个速度来说太慢了吗?

3 个答案:

答案 0 :(得分:2)

这很慢,因为你正在调用两个程序, xxdtee, 在循环的每次迭代中。

使用printf内置内容应该更加环路友好,并且只需要tee的一个实例:

tr -s " " '\n' < hex.txt | 
while read seq; do printf "\x$seq"; done |
tee -a ascii 

(您可能不再需要-a切换到tee了。

( 如果您想使用脚本语言,rubyawk旁边的另一个不错的选择:

tr -s " " '\n' < hex.txt |  ruby -pe '$_ = $_.to_i(16).chr'

(比in-bash版本快得多)。 )

答案 1 :(得分:2)

尝试以下操作 - 不幸的是,解决方案因使用的awk实施而异:

# BSD/OSX awk
xargs printf '0x%s ' < hex.txt | awk -v RS=' ' '{ printf "%c", $0 }' > ascii

# GNU awk; option -n needed to support hex. numbers
xargs printf '0x%s ' < hex.txt | awk -n -v RS=' ' '{ printf "%c", $0 }' > ascii

# mawk - sadly, printf "%c" only works with letters and numbers if the input is *hex*
awk  -v RS=' ' '{ printf "%c", int(sprintf("%d", "0x" $0)) }' < hex.txt

使用2MB输入文件,我2012年末的iMac配备3.2 GHz Intel Core i5和运行OSX 10.10.3的Fusion Drive的时间如下:

  • BSD / OSX awk:ca。 1s
  • GNU awk:ca。 0.6s
  • mawk :ca。 0.5s

将此与PSkocik's optimized-bash-loop solution对比:ca。 11s

考虑到mawk解决方案,如果没有管道的单一命令,那么它应该是更快的解决方案< em>所有 awk实现,但实际上它不是。这是一个适用于所有三个实现的版本,其中-n用于 GNU awk按需提供:awk $([[ $(gawk --version 2>/dev/null) = GNU* ]] && printf %s -n) -v RS=' ' '{ printf "%c", int(sprintf("%d", "0x" $0)) }' < hex.txt

速度增加来自完全避免bash循环并让实用程序完成工作:

  • xargs printf '0x%s ' < hex.txthex.txt0x的所有值添加前缀,以便awk稍后将其识别为十六进制。
    • 请注意,根据您的平台,xargs使用所有stdin输入标记作为参数构造的命令行可能超过getconf ARG_MAX报告的最大命令行长度 - 幸运的是,{{1足够智能,然后调用命令多次次,每次在命令行上尽可能多地填充参数。
  • xargs
    • awk -v RS=' ' '{ printf "%c", $0 }'读取每个以空格分隔的标记 - 即每个十六进制。值 - 作为单独的输入记录
    • awk -v RS=' '然后只需使用printf "%c", $0将每条记录转换为ASCII字符等效。

一般来说:

    具有较大迭代次数的
  • Bash循环 本质上缓慢
  • 如果您在每次迭代中调用外部实用程序 更糟
    请参阅下面的Charles Duffy的评论。

为了获得具有较大迭代次数的良好性能,避免使用bash循环并让外部实用程序执行迭代工作

答案 2 :(得分:0)

好吧,您可以删除第一个cat并将其替换为tr < hex.txt。然后,您还可以构建静态转换表并删除echoxxd。但是循环仍然很慢,我认为你无法摆脱它。