评估

评估
$ FILE="$(mktemp)"
$ printf "a\0\n" > "$FILE"
$ od -tx1z "$FILE"
0000000 61 00 0a                                         >a..<
0000003

到目前为止,一切都很好。

我将上面的内容封装到 bash 脚本中

#! /bin/bash

cmd=("$@")
FILE="$(mktemp)"
eval "${cmd[@]}" > "$FILE"
od -tx1z "$FILE"

$ script printf 'a\0\n' 
0000000 61 30 6e                                         >a0n<
0000003

为什么输出会变成\0文字字符串?我怎样才能防止这种情况发生?


在这篇文章中并不是很重要:我的问题来自于我试图将一些命令包装到 bash 脚本中,所以防止命令扩展删除 NUL:

FILE="$(mktemp)"
printf "a\0\n" > "$FILE"
S="$(uuencode -m "$FILE" /dev/stdout)"
uudecode -o /dev/stdout <(printf "$S") | od -tx1
rm "$FILE"

答案1

为什么输出会变成\0文字字符串?我怎样才能防止这种情况发生?

因为您使用了eval,这增加了另一个级别的 shell 处理。eval运行命令printf a\0\n,其中反斜杠转义零和n,保留它们原样并删除反斜杠。

你可以通过很好地防止这种情况,而不是使用eval.仅使用"$@" > "$FILE" 应该可以运行作为脚本参数给出的命令。但在这种情况下,您不能像使用eval.或者,您可以重新设计整个事情,这样您就不需要将命令作为参数传递。

我正在尝试将一些命令包装到 bash 脚本中,以防止命令扩展删除 NUL:

S="$(uuencode -m "$FILE" /dev/stdout)"

这是这里的问题吗?uuencode -m不应产生任何 NUL 字节。恰恰相反,因为它将二进制数据编码为文本。

最后一个脚本将一个a、一个 NUL 和一个换行符写入$FILE并将它们传递给,这将打印出这些、 或类似内容od的十六进制表示形式。0000000 61 00 0a

答案2

您可以将 NUL 输出到 stdout、stderr 或从 stdin 接收它。
任何“内部”捕获并且(几乎)任何 NUL 的使用都是一个问题。

您可以向 stdout 发出 NUL:

$ printf 'a\0b' | sed -n l
a\000b$

或者:

$ printf 'a\0b' | cat -A
a^@b

还有一个文件:

$ printf 'a\0b' >outfile

确切地这也适用于脚本。

评估

您的第一个脚本的问题是它正在使用eval

$ cmd=(printf 'a\0b\n')
$ echo "${cmd[@]}"
printf a\0b\n

$ eval echo "${cmd[@]}"
printf a0bn

在第二个循环中,shell 行解析反斜杠被删除。
只是不要使用 eval:

$ "${cmd[@]}"| sed -n l
a\000b$

扩展

这证明内置的printf和都stdout能够使用 NUL。

但这失败了:

$ printf '%s\n' "$(printf 'a\0b')" | cat -A
bash: warning: command substitution: ignored null byte in input
ab$

甚至(在 bash 4.4+ 中)还有一条警告消息。

简而言之,shell(大多数 shell)内部使用 C 字符串,C 字符串以第一个 NUL 结尾。一些 shell 在“命令扩展”中在第一个 NUL 处切割。

$ ksh -c 'echo $(printf "a\0b\n")|sed -n l'    # also yash
a$

有些删除了NUL

$ bash -c 'echo $(printf "a\0b\n")|sed -n l'
ab$

有些甚至将 NUL 更改为空格:

$ zsh -c 'echo $(printf "a\0b\n")|sed -n l'
a b$

类似的问题也发生在变量的赋值上。

编码

是的,您链接的答案用于uuencode编码(和解码)文件内容。

一种更简单的方法似乎是使用xxd(可以反转八进制转储):

FILE="$(mktemp)"
printf "a\0b\n" > "$FILE"
S=$(xxd -p "$FILE")
xxd -r -p <(printf '%s' "$S") | xxd -p
rm "$FILE"

相关内容