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

ant*_*lex 26 bash null-character

在尝试通过使用NULL字符作为分隔符来正确处理文件//foldernames列表(请参阅我的其他问题)时,我偶然发现了Bash的一个奇怪的行为,我不明白:

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

例如,

echo -ne "n\0m\0k" | od -c   # -> 0000000   n  \0   m  \0   k
Run Code Online (Sandbox Code Playgroud)

但:

VAR1=`echo -ne "n\0m\0k"`
echo -ne "$VAR1" | od -c   # -> 0000000   n   m   k
Run Code Online (Sandbox Code Playgroud)

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

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

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

小智 32

在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
Run Code Online (Sandbox Code Playgroud)


vaa*_*aab 18

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

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

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

  • 在管道中
  • 在文件中

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

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

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

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

绕过限制

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

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

quote() { sed 's/\\/\\\\/g;s/\x0/\\x00/g'; }
Run Code Online (Sandbox Code Playgroud)

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

例:

## 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"
Run Code Online (Sandbox Code Playgroud)

注意:用于| hd以十六进制查看数据的干净视图,并检查是否没有丢失任何NUL字符.

改变工具

记住,你可以在管道中使用管道而不使用变量或参数,但不要忘记例如<(command ...)将创建命名管道的构造(一种临时文件).

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

EDIT2:第二个执行的quote,因为只使用过的错误\0比实际会吃掉更多的零为\0,\00,\000\0000是等价的.所以\0被替换为\x00.感谢@MatthijsSteen发现这个.


Cir*_*四事件 9

使用uuencodeuudecode用于POSIX可移植性

xxd并且base64 不是POSIX 7但是uuencode是.

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

输出:

0000000 61 00 0a
0000003
Run Code Online (Sandbox Code Playgroud)

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

所以我想真正的答案是:不要使用Bash,使用Python或其他一些更理智的解释语言.