将包含空字符(\ 0)的字符串分配给Bash中的变量

时间:2011-07-04 10:57:53

标签: bash null-character

尝试通过使用NULL字符作为分隔符来正确处理文件/ / foldernames列表(see my other questions)时,我偶然发现了一个我不理解的Bash奇怪的行为:

将包含一个或多个NULL字符的字符串分配给变量时,会丢失/忽略/不存储NULL字符。

例如,

echo -ne "n\0m\0k" | od -c   # -> 0000000   n  \0   m  \0   k

可是:

VAR1=`echo -ne "n\0m\0k"`
echo -ne "$VAR1" | od -c   # -> 0000000   n   m   k

这意味着我需要将该字符串写入文件(例如,在/ tmp中),如果不希望或不可行,直接从那里读取它。

Z shell(zsh)中执行这些脚本时,在两种情况下都会保留包含\ 0的字符串,但遗憾的是我不能假设在运行我的脚本的系统中存在zsh而Bash应该是。< / p>

如何在不丢失任何(元)字符的情况下有效地存储或处理包含\ 0字符的字符串?

4 个答案:

答案 0 :(得分:31)

在Bash中,您不能将NULL字符存储在变量中。

但是,您可以使用xxd命令存储数据的纯十六进制转储(稍后再次反转此操作)。

VAR1=`echo -ne "n\0m\0k" | xxd -p | tr -d '\n'`
echo -ne "$VAR1" | xxd -r -p | od -c   # -> 0000000    n  \0   m  \0   k

答案 1 :(得分:17)

正如其他人已经说过的那样,你无法存储/使用NUL字符

  • 在变量中
  • 在命令行的参数中。

但是,你可以处理任何二进制数据(包括NUL字符):

  • in pipes
  • in files

所以回答你的上一个问题:

  

任何人都可以给我一个暗示如何包含\ 0字符的字符串   有效地存储或处理而不会丢失任何(元)字符?

您可以使用文件或管道来有效地存储和处理包含任何元字符的任何字符串。

如果您打算处理数据,则还应注意:

绕过限制

如果你想使用变量,那么你必须通过编码来摆脱NUL char,而这里的各种其他解决方案提供了巧妙的方法(显而易见的方法是使用例如base64编码/解码)。 / p>

如果您担心内存或速度,您可能希望使用最小解析器并仅引用NUL字符(和引号字符)。在这种情况下,这可以帮助您:

quote() { sed 's/\\/\\\\/g;s/\x0/\\x00/g'; }

然后,您可以在将数据存储到变量和数据之前保护数据 命令行参数,将敏感数据传送到quote,这将输出没有NUL字符的安全数据流。你可以回来 原始字符串(使用NUL字符)使用echo -en "$var_quoted",它将在标准输出上发送正确的字符串。

示例:

## Our example output generator, with NUL chars
ascii_table() { echo -en "$(echo '\'0{0..3}{0..7}{0..7} | tr -d " ")"; }
## store
myvar_quoted=$(ascii_table | quote)
## use
echo -en "$myvar_quoted"

注意:使用| hd以十六进制格式获取数据的清晰视图 检查你没有丢失任何NUL字符。

更改工具

请记住,在命令行中不使用变量或参数的情况下,您可以使用管道,但不要忘记将创建命名管道的<(command ...)构造(一种临时文件)。

编辑: quote的第一个实现不正确,无法正确处理\解释的echo -en个特殊字符。感谢@xhienne发现这一点。

EDIT2: quote的第二个实现因为仅使用\0而导致错误而不是实际占用更多的零\0\00 },\000\0000是等效的。因此\0\x00取代。感谢@MatthijsSteen发现这个。

答案 2 :(得分:8)

使用uuencodeuudecode获取POSIX可移植性

xxdbase64 are not POSIX 7,但uuencode is

VAR="$(uuencode -m <(printf "a\0\n") /dev/stdout)"
uudecode -o /dev/stdout <(printf "$VAR") | od -tx1

输出:

0000000 61 00 0a
0000003

不幸的是,除了写入文件之外,我没有看到Bash进程<()替换扩展的POSIX 7替代方案,默认情况下它们没有安装在Ubuntu 12.04中(sharutils包)。

所以我猜真正的答案是:不要使用Bash,使用Python或其他一些更为理解的语言。

答案 3 :(得分:3)

我爱jeff's answer。我会使用Base64编码而不是xxd。它节省了一点空间,并且(我认为)可以更清楚地了解它的用途。

VAR=$(echo -ne "foo\0bar" | base64)
echo -n "$VAR" | base64 -d | xargs -0 ...

对于-e,需要一个带有编码空值('\ 0')的文字字符串的回声,虽然我似乎还记得一些关于“echo -e”如果你回应任何东西是不安全的用户输入,因为他们可以注入回声将解释并最终导致坏事的转义序列。将已编码的存储字符串回显到解码中时,不需要-e标志。