bash 无法在变量中存储十六进制值 0x00

bash 无法在变量中存储十六进制值 0x00

我正在尝试用 dd 做一些技巧。我认为可以将一些十六进制值存储在名为“header”的变量中,以将其通过管道传输到 dd 中。

我没有变量的第一步是这样的:

$ echo -ne "\x36\xc9\xda\x00\xb4" |dd of=hex
$ hd hex

00000000  36 c9 da 00 b4                                    |6....|
00000005

之后我尝试了这个:

$ header=$(echo -ne "\x36\xc9\xda\x00\xb4") 
$ echo -n $header | hd

00000000  36 c9 da b4                                       |6...|
00000004

正如你所看到的,我失去了变量的\x00$header。有人对这种行为有解释吗?这真让我抓狂。

答案1

您不能在字符串中存储空字节,因为 Bash 使用 C 样式字符串,它为终止符保留空字节。因此,您需要重写脚本以简单地通过管道传输包含空字节的序列,而 Bash 不需要将其存储在中间。例如,您可以这样做:

printf "\x36\xc9\xda\x00\xb4" | hd

顺便说一句,请注意,您不需要echo;您可以使用 Bashprintf来完成许多其他简单的任务。

或者,您可以使用临时文件来代替链接:

printf "\x36\xc9\xda\x00\xb4" > /tmp/mysequence
hd /tmp/mysequence

当然,这有一个问题,即该文件/tmp/mysequence可能已经存在。现在您需要继续创建临时文件并将其路径保存在字符串中。

或者您可以通过使用进程替换来避免这种情况:

hd <(printf "\x36\xc9\xda\x00\xb4")

操作<(command)符在文件系统中创建一个命名管道,它将接收command.hd将接收该管道的路径作为其第一个参数——它将打开并读取该管道几乎就像任何文件一样。你可以在这里读更多关于它的内容:https://unix.stackexchange.com/a/17117/136742

答案2

您可以使用zsh唯一可以在其变量中存储 NUL 字符的 shell。该字符甚至恰好位于$IFSin的默认值中zsh

nul=$'\0'

或者:

nul=$'\x0'

或者

nul=$'\u0000'

或者

nul=$(printf '\0')

但请注意,您不能将此类变量作为参数或环境变量传递给以下命令:被处决因为参数和环境变量是传递给execve()系统调用的 NUL 分隔字符串(系统 API 的限制,而不是 shell)。然而,在 中zsh,您可以将 NUL 字节作为参数传递给函数或内置命令。

echo $'\0' # works
/bin/echo $'\0' # doesn't

答案3

Bash 内部使用 C 字符串,无法存储空字节。将值存储在临时文件中,如下所示:

    zHex=$(mktemp --tmpdir "$(basename "$0")-XXXX")
    trap "rm -f ${zHex@Q}" EXIT

变量 zHex 现在包含唯一的文件名。 $zHex 引用的文件可以手动删除,但当程序因任何原因终止时,该文件将被自动删除。

然后像这样使用变量:

    echo -ne "\x36\xc9\xda\x00\xb4" > "$zHex"
    hd "$zHex"

这不会将带有空字节的值存储到变量中。相反,它使用变量来存储文件名。该文件与任何其他文件一样,可能包含空字节并且可以反复使用。文件本身很可能永远不会被物理写入磁盘。

通过陷阱,bash 会自动删除该文件,因此您不必担心手动删除它,除非您正在创建一个疯狂的垃圾数组。由于 RAM 缓冲,该技术相当快。

答案4

由于回车符可以是文件名的一部分,因此我喜欢使用空终止列表。但我无法存储带有空字节的字符串,因为 bash 因为 Bash 将字符串存储为简单的 C 字符串,其中空字节是字符串终止符,因此不能是字符串本身的一部分。

为了解决这个问题,我创建了一个字符串数组,其中假定每个元素后都存在空字节。列表本身显然包含空字节。我将包含空字节的值存储在这样的数组中...

    readarray -d $'\0' zArray < <(null_terminated_list_maker)

然后,我可以像这样使用空字节重现该值......

    [[ "${zArray[*]}" ]] && printf '%s\0' "${zArray[@]}"

通过这种方式,bash 数组可用于存储任何包含空字节的值。

目的[[ "${zArray[*]}" ]]是查看数组是否有任何值(空字符串是一个值)。该测试解决了以下问题:如果像这样将空数组传递给 printf,则 printf 将打印一个空字节,这是错误的。它不应该打印任何内容。

当表示完全任意的数据时,存在一个问题:您的输入实际上是否以空字节终止?该方法需要扩展以处理可能或可能不以空字节结尾的数据。

相关内容