1

1

因此,我想将 binaryFile.dd 的前 512 个字节作为第二个参数传递给 myProgram,但 bash 会删除所有 NUL 字符。有什么办法可以在 bash 中避免这种情况,还是我什么都隐藏?

myProgram parameter1 "$(head -c 512 binaryFile.dd)"

答案1

无法在命令参数中传递空字节。这并不是因为 bash 的限制,尽管 bash 也有这个限制。这是运行命令的接口的限制:它将空字节视为参数的结尾。没有逃避机制。

大多数 shell 不支持变量或函数和内置函数的参数中的空字节。 Zsh 是一个值得注意的例外。

$ ksh -c 'a=$(printf foo\\0bar); printf "$a"' | od -t x1
0000000 66 6f 6f
0000003
$ bash -c 'a=$(printf foo\\0bar); printf "$a"' | od -t x1
0000000 66 6f 6f 62 61 72
0000006
$ zsh -c 'a=$(printf foo\\0bar); printf "$a"' | od -t x1
0000000 66 6f 6f 00 62 61 72
0000007

但即使使用 zsh,如果您尝试将参数传递给外部命令,那么空字节后面的任何内容都会被忽略 - 不是由 zsh 而是由内核忽略。

$ zsh -c 'a=$(printf foo\\0bar); /usr/bin/printf "$a"' | od -t x1
0000000 66 6f 6f
0000003

如果要将空字节传递给程序,则需要找到命令行参数以外的其他方法。

head -c 512 binaryFile.dd | myProgram --read-parameter2-from-stdin parameter1
myProgram --read-parameter2-from-file=<(head -c 512 binaryFile.dd) parameter1

答案2

bash不太适合直接处理二进制数据。

可以将二进制数据与文件一起使用,也可以使用表示数据的十六进制字符串。

要转换为十六进制,您可以使用hexdump, xxd, od

例如,要将 512 字节转换为十六进制字符串,请使用

xxd -ps -c 512 file.bin

将其转换回二进制使用

echo "$myhexstring" | xxd -r -ps > file.bin

答案3

不,bash 中的字符串不可能包含 NUL ( \0)。
因此,变量(因为它包含字符串)不能包含 NUL。

原因是 bash 是用c“字符串以 NUL 结尾”的范式编写的。[1] Linux内核也施加了这样的限制。[2]但即使内核允许在字符串[3](参数)中使用 NUL,大多数 shell,尤其是 bash,也不能在变量[4]中包含 NUL 。

位置参数($1$2等)相当于变量,也不能包含 NUL。

然而,nul 可以存在于文件、流和 printf 中:

$ printf 'test\0nuls\n' | od -vAn -tx1c
  74  65  73  74  00  6e  75  6c  73  0a
   t   e   s   t  \0   n   u   l   s  \n

正如您所看到的,printf 创建了一个 NUL 并且它流经管道 ( |)。但 NUL 是从“命令执行”中剥离出来的:

$ echo $(printf 'test\0nuls\n') | od -vAn -tx1c
bash: warning: command substitution: ignored null byte in input
  74  65  73  74  6e  75  6c  73  0a
   t   e   s   t   n   u   l   s  \n

在 bash 4.4 中它甚至会发出警告。在这种情况下,zsh 会默默地将 NUL 替换为空格:

$ zsh -c ' echo $(printf "test\0nuls\n") | od -vAn -tx1c'
  74  65  73  74  20  6e  75  6c  73  0a
   t   e   s   t       n   u   l   s  \n

我们可以使用 printf 以及包含 NUL 的文件的catheadtaildd 部分创建一个包含 NUL 的文件:

$ printf 'test\0nuls\0in\0files\0\n' > testnul.bin
$ cat testnul.bin | xxd -ps
74657374006e756c7300696e0066696c6573000a

$ head -c 7 testnul.bin | xxd -ps
74657374006e75

$ dd if=testnul.bin bs=7 count=1 | xxd -ps
74657374006e75
1+0 records in
1+0 records out
7 bytes copied, 0.000655689 s, 10.7 kB/s

$ dd if=testnul.bin bs=7 count=1 2>/dev/null| xxd -ps
74657374006e75

就您而言,没有简单的[5]方法可以将二进制文件的内容作为参数。也许十六进制表示可以工作:

$ myProgram "$parameter1" "$(xxd -ps -c 512 binaryFile.dd)"

感谢@Gilles 所做的所有额外工作(和细节)。

1

[1] 一切都归结为旧的的定义C string«字符串以 NUL ( \0) 结尾»。该范例已在多个C库和工具中进行编码,其中 POSIX 有几个示例。喜欢strcpy 在这里其中指出(强调我的):

strcpy() 函数应复制 s2 指向的字符串(包括终止 NUL 字符) 到 s1 指向的数组中。

这意味着假定字符串以 NUL 结尾。
或者,换句话说,可能只有一个 NUL,即最后一个。

2

[2]系统execve()调用,也在 POSIX 中定义,期望字符串(命令参数)以 NUL 结尾。这就是为什么即使 shell 可以与 NUL 一起使用(大多数都不能,但 zsh 是一个明显的例外):

$ zsh -c 'a=$(printf "included\0null"); printf "$a"' | od -vAn -tx1c
  69  6e  63  6c  75  64  65  64  00  6e  75  6c  6c
   i   n   c   l   u   d   e   d  \0   n   u   l   l

不是在调用传递的参数中使用 NUL execve()

$ zsh -c 'a=$(printf "included\0null"); /usr/bin/printf "$a"' | od -vAn -tx1c
  69  6e  63  6c  75  64  65  64
   i   n   c   l   u   d   e   d

3

[3]但即使内核能够在参数中包含 NUL,bash 也不允许它们:

$ bash -c 'a=$(printf "included\0null"); /usr/bin/printf "$a"' | od -vAn -tx1c
bash: warning: command substitution: ignored null byte in input
  69  6e  63  6c  75  64  65  64  6e  75  6c  6c
   i   n   c   l   u   d   e   d   n   u   l   l

在 bash 4.4 中,当 NUL 被删除时,它甚至会发出警告。

4

[4] 大多数 shell,尤其是 bash,不能在变量中包含 NUL。

$ printf 'included\0null' | od -vAn -tx1c
  69  6e  63  6c  75  64  65  64  00  6e  75  6c  6c
   i   n   c   l   u   d   e   d  \0   n   u   l   l

$ printf 'included\0null' | ( read a; printf '%s\n' "$a" | od -vAn -tx1c )
  69  6e  63  6c  75  64  65  64  6e  75  6c  6c
   i   n   c   l   u   d   e   d   n   u   l   l

如果正在运行的 shell 是 zsh,则这(相反)将使用 null:

$ zsh -c 'printf "included\0null" | ( read a; printf "%s\n" "$a" | od -vAn -tx1c )'
  69  6e  63  6c  75  64  65  64  00  6e  75  6c  6c  0a
   i   n   c   l   u   d   e   d  \0   n   u   l   l  \n

5

[5] 这意味着“直接”(简单)包含值 0 ( ) 的字节\0是不可能的。但是,使用十六进制、基数 64 或某种等效形式的 C-string 编码(复杂),$'\0'可以包含零值。

相关内容