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

问题描述 投票:26回答:4

在尝试通过使用NULL字符作为分隔符来正确处理文件/文件夹名称列表(qazxsw poi)时,我偶然发现了一个我不理解的Bash奇怪的行为:

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

例如,

see my other questions

但:

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

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

VAR1=`echo -ne "n\0m\0k"` echo -ne "$VAR1" | od -c # -> 0000000 n m k (zsh)中执行这些脚本时,在两种情况下都会保留包含\ 0的字符串,但遗憾的是我不能假设在运行我的脚本的系统中存在zsh,而Bash应该是。

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

bash null-character
4个回答
31
投票

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

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

xxd

17
投票

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

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

但是,您可以处理任何二进制数据(包括NULL char):

  • 在管道中
  • 在文件中

那么回答你的上一个问题:

任何人都可以给我一个提示,如何在不丢失任何(元)字符的情况下有效地存储或处理包含\ 0字符的字符串?

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

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

  • 只有NUL char才会被命令行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 的变量和参数吃掉。
  • 警惕命令替换(如you can check this$(command..))作为`command..`变量有一个额外的扭曲。

绕过限制

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

如果你担心内存或速度,你可能想要使用一个最小的解析器,只引用NUL字符(和引号字符)。在这种情况下,这将有助于您:

it'll eat your ending new lines

然后,您可以在将数据存储到变量和命令行参数之前保护数据,方法是将敏感数据传输到quote() { sed 's/\\/\\\\/g;s/\x0/\\x00/g'; } ,这将输出没有NUL字符的安全数据流。您可以使用quote返回原始字符串(使用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发现这一点。

编辑2:quote的第二次实施有bug,因为只使用\0而不是实际吃掉更多的零,因为\0\00\000\0000是等价的。所以\0\x00取代。感谢@MatthijsSteen发现这个。


8
投票

使用uuencodeuudecode获得POSIX可移植性

xxdbase64 are not POSIX 7uuencode 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
投票

我喜欢jeff's answer。我会使用Base64编码而不是xxd。它节省了一点空间,并且(我认为)可以更清楚地识别出什么是预期的。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.