zsh - 错误的数学表达式:非法字符:^A - 尝试从文件中读取 0x01 时

zsh - 错误的数学表达式:非法字符:^A - 尝试从文件中读取 0x01 时

我正在 zsh 中读取二进制文件的前四个字节。前 3 个字节是0x00,第 4 个字节是0x01。我通过将文件作为字符串读入,然后对其进行拆分和切片,创建了这些字节的数组,结果如下:

>  echo -n "${(j::)bytes}" | xxd
00000000: 0000 0001                                ....

现在我尝试从最右边(第 4 个字节)开始使用适当的幂将它们相加以得到总和。问题是,当我尝试在算术评估或整数变量中使用第 4 个字节时:

integer -i 10 b=${bytes[-1]} # This is the offending line
sum=$((sum + b * (256 ** i)))

我收到以下错误:

 bad math expression: illegal character: ^A

也就是0x01

我如何告诉 zsh 将字节解释为数字?我注意到(#)扩展标志,它强制将表达式解释为数字,然后解释为字符,但我遇到了相反的问题。它似乎被解释0x01为一个字符。

更新:

我怀疑问题在于数组的类型是字符(如果 zsh 集合有类型?)因为我使用(s::)整个文件的字符串创建了它,但我不确定如何将数组元素转换回整数。我试过

b=$(printf '%d' "$bytes[4]")

但这给了我一个类似的错误。我似乎可以通过以下任何一种方法重现:

printf '%d' $'^A'
integer b=$'^A'

其中由两个文字字符和^A组成。^A

答案1

内置%d运算符printf会将字符转换为其数值,但它需要一个前导引号来区分输入参数和算术表达式:

>> one=$'\1'
>> typeset -p one
typeset one=$'\C-A'
>> printf '%d\n' "'$one"  # leading single quote
1
>> printf '%d\n' "' "     # quote and space
32
>> printf '%d\n' '"A'     # leading double-quote
65

>> # your input values:
>> bytes=($'\0' $'\C-@' ${(#):-0} $'\C-A')
>> typeset -p bytes
typeset -a bytes=( $'\C-@' $'\C-@' $'\C-@' $'\C-A' )
>> integer b=$(printf '%d' "'$bytes[4]")
>> print "value in b: $b"
value in b: 1

请注意,中的值b是一个字符串,即使它被声明为integer。整数类型集只会改变变量在赋值和扩展中的处理方式 - 它不会改变内部表示。


要转换整个字节数组,请使用-v(分配变量) 选项printf

>> setopt nomultibyte
>> typeset -a bin=($'\C-@'  $'\C-C' ' ' 'A' '~' ${(#):-240})
>> typeset -a nums
>> printf -v nums %d \'${^bin}
>> typeset -p nums
typeset -a nums=( 0 3 32 65 126 240 )

部分作品:

  • typeset -a nums- 输出数组。这被声明为一个数组,以便printf将每个结果分配给单独的数组元素。从 文档

    -v 选项使输出存储为参数的值姓名,而不是打印的。如果姓名是一个数组,并且在使用参数时重复使用格式字符串,那么每次使用格式字符串时都会使用一个数组元素。

  • \'${^bin}- 这将在数组中的每个元素前加上单引号,使用${^...} rc_expand_param选项。
  • setopt nomultibyte- 这样,shell 操作就会将每个字节视为单个字符。处理单字节数组时,这并非绝对必要,但它可以确保显示和赋值时的一致性。

相关内容