我有一个 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 对此太慢了?
最佳答案
尝试以下 - 不幸的是,解决方案因 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 和 Fusion Drive,运行 OSX 10.10.3 时序如下:
- BSD/OSX awk:大约。
1s
- GNU awk:ca。
0.6s
- mawk:大约
0.5s
将此与 PSkocik's optimized-bash-loop solution 对比:约。 11s
很容易认为 mawk
解决方案,鉴于它是一个没有管道的单个命令,应该是使用all 的更快的解决方案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.txt
为hex.txt
中的所有值添加前缀与0x
这样awk
稍后会将它们识别为十六进制。- 请注意,根据您的平台,
xargs
的命令行使用所有 stdin 输入标记作为参数的构造可能超过getconf ARG_MAX
报告的最大命令行长度- 幸运的是,xargs
足够聪明,然后 多次 调用命令,每次都在命令行上安装尽可能多的参数。
- 请注意,根据您的平台,
-
awk -v RS=' ' '{ printf "%c", $0 }'
-
awk -v RS=' '
读取每个以空格分隔的标记 - 即每个十六进制。值 - 作为单独的输入记录 -
printf "%c", $0
然后使用printf
将每条记录简单地转换为其对应的 ASCII 字符.
-
一般来说:
-
具有大量迭代次数的
- Bash 循环本质上很慢。
- 如果您还在每次迭代中调用外部实用程序,情况会变得更糟。
请参阅下面 Charles Duffy 的评论。
为了在大量迭代次数下获得良好的性能,避免 bash 循环并让外部实用程序执行迭代工作。
关于bash - 在 native bash 中性能合理的十六进制解码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30200479/