在本机bash中合理执行十六进制解码?

在本机bash中合理执行十六进制解码?,bash,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 或 两者都是极其缓慢的 我突然启动了一个C程序,该程序在大约两秒钟内转换了文件,后来意识到我可以这样做: cat hex.txt | xxd -r -p 因为我已经转换了文件并找到了最

我有一个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

两者都是极其缓慢的

我突然启动了一个C程序,该程序在大约两秒钟内转换了文件,后来意识到我可以这样做:

cat hex.txt | xxd -r -p

因为我已经转换了文件并找到了最佳解决方案,所以我的问题不是转换过程本身,而是如何优化我的前两次尝试,就好像第三次不可能一样。有什么方法可以加快这些单行程序的速度,或者Bash的速度太慢了吗?

好吧,你可以放下第一个
cat
,用
tr
替换它。然后您还可以构建一个静态转换表并删除
echo
xxd
。但是循环仍然很慢,我想你无法摆脱它。

它很慢,因为你调用了两个程序,
xxd
tee
, 在循环的每个迭代中

使用
printf
内置应该更方便循环,并且只需要一个
tee的实例即可:

tr -s " " '\n' < hex.txt | 
while read seq; do printf "\x$seq"; done |
tee -a ascii 
(比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
#BSD/OSX awk
xargs printf'0x%s'ascii
#GNU-awk;选项-n需要支持十六进制。数字
xargs printf'0x%s'ascii
#mawk-遗憾的是,如果输入为*十六进制,printf“%c”只适用于字母和数字*
awk-vrs=''{printf“%c”,int(sprintf(“%d”,“0x”$0))}'
使用2MB输入文件,我的2012年末iMac采用3.2 GHz Intel Core i5和Fusion驱动器,运行OSX 10.10.3,时间安排如下:

  • BSD/OSX awk:ca.
    1s
  • GNU awk:ca.
    0.6s
  • mawk:ca.
    0.5s
将此与:ca.
11s

考虑到
mawk
解决方案是一个没有管道的单一命令,人们很容易认为它应该是所有
awk
实现的更快的解决方案,但实际上并非如此。这里有一个版本可以与按需提供的GNU awk的
-n
的所有三个实现一起工作:
awk$([[$(gawk--version 2>/dev/null)=GNU*]]&&printf%s-n)-vrs=''{printf“%c”,int(sprintf(“%d”,“0x”$0))}

速度的提高来自于完全避免
bash
循环,并让实用程序完成工作:

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

一般来说:

  • Bash循环迭代次数大,本质上是缓慢的
  • 如果在每次迭代中调用外部实用程序,情况会变得更糟 见下面查尔斯·达菲的评论

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

我认为bash循环不是很快;您正在为每一行输入运行一个新程序。这就是瓶颈,您只需调用
xxd
就解决了它。除此之外,前两次尝试的速度不会明显加快。顺便说一句,确实存在一些能够执行本机循环的shell,但执行本机循环的速度并不那么慢——如果您有一个合适的David Korn ksh93构建(第三方克隆的性能各不相同),那么性能数字接近awk的性能数字并非闻所未闻。然而,Bash根本不是为速度而构建的。任何时候你想键入
cat |..
STOP,你就在进行uooc(不必要地使用cat)。重定向保存生成一个单独的子shell。(例如,
xxd-r-p,其中性能不是问题(小文件和循环之外的任何内容)我倾向于或更喜欢使用
cat |
来尽可能地保持代码从左到右的可读性。在这种情况下,使用
cat
只会在表达式开始计算循环之前引起一个小的延迟;因为它是在循环之外使用的,在这种情况下,它对性能没有影响。它至少会快几个数量级当然,没有C版本快,但是……好吧,问题是什么足够快。:)我同意这两点,但是第二个(外部实用程序调用)比第一个(固有的低性能)差几个数量级……当我说“数量级”时,我的意思是“每秒数十万次迭代”与“每秒超过一千次迭代”“因此,在我自己非常不科学的地方基准中,以10为基数的两个数量级,或以2为基数的八个数量级;在一种情况下,循环只增加一个计数器;另一个调用立即退出的外部实用程序,然后递增计数器。效果很好
tr -s " " '\n' < hex.txt |  ruby -pe '$_ = $_.to_i(16).chr'
# 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