我有一个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对于这个速度来说太慢了吗?
答案 0 :(得分:2)
这很慢,因为你正在调用两个程序,
xxd
和tee
,
在循环的每次迭代中。
使用printf
内置内容应该更加环路友好,并且只需要tee
的一个实例:
tr -s " " '\n' < hex.txt |
while read seq; do printf "\x$seq"; done |
tee -a ascii
(您可能不再需要-a
切换到tee
了。
(
如果您想使用脚本语言,ruby
是awk
旁边的另一个不错的选择:
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的时间如下:
1s
0.6s
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.txt
为hex.txt
中0x
的所有值添加前缀,以便awk
稍后将其识别为十六进制。
xargs
使用所有stdin输入标记作为参数构造的命令行可能超过getconf ARG_MAX
报告的最大命令行长度 - 幸运的是,{{1足够智能,然后调用命令多次次,每次在命令行上尽可能多地填充参数。xargs
awk -v RS=' ' '{ printf "%c", $0 }'
读取每个以空格分隔的标记 - 即每个十六进制。值 - 作为单独的输入记录awk -v RS=' '
然后只需使用printf "%c", $0
将每条记录转换为ASCII字符等效。一般来说:
为了获得具有较大迭代次数的良好性能,避免使用bash循环并让外部实用程序执行迭代工作。
答案 2 :(得分:0)
好吧,您可以删除第一个cat
并将其替换为tr < hex.txt
。然后,您还可以构建静态转换表并删除echo
和xxd
。但是循环仍然很慢,我认为你无法摆脱它。