$ 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"