23

允许扩展。我的说:$'string'man bash

形式的词被特殊对待。单词扩展为,用 ANSI C 标准指定的反斜杠转义字符替换。反斜杠转义序列(如果存在)按如下方式解码: alert (bell) backspace 转义字符 换行符 换行符 回车 水平制表符 垂直制表符 反斜杠 单引号 双引号 八位字符,其值为八进制值(一到三数字) 八位字符,其值为十六进制值$'string'string
\a
\b
\e
\E
\f
\n
\r
\t
\v
\
\'
\"
\nnnnnn
\xHHHH(一个或两个十六进制数字) 一个控制字符
\cxx

扩展的结果是单引号的,就好像美元符号不存在一样。

但是为什么不将$'\0'and$'\x0'转换为空字符呢?
是否记录在案?有原因吗?(它是一个特性还是一个限制,甚至是一个错误?)

$ hexdump -c <<< _$'\0'$'\x1\x2\x3\x4_'
0000000   _ 001 002 003 004   _  \n
0000007

echo给出预期的结果:

> hexdump -c < <( echo -e '_\x0\x1\x2\x3_' )
0000000   _  \0 001 002 003   _  \n
0000007

我的 bash 版本

$ bash --version | head -n 1
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

为什么echo $'foo\0bar'不表现为echo -e 'foo\0bar'

4

3 回答 3

27

这是一个限制。bash不允许字符串值包含内部 NUL 字节。

Posix(和 C)字符串不能包含内部 NUL。例如,参见字符串的Posix 定义(添加了重点):

3.92 字符串

由第一个空字节终止并包括在内的连续字符序列。

类似地,标准 C 对字符串中的 NUL 字符相当明确:

§5.2.1p2 …所有位都设置为 0 的字节,称为空字符,应存在于基本执行字符集中;它用于终止一个字符串。

Posix 明确禁止/在文件名(XBD 3.170)或环境变量(XBD 8.1“...被认为以空字节结尾”中使用 NUL(和)。

在这种情况下,包括 bash 在内的 shell 命令语言倾向于使用与字符串相同的定义,作为由单个 NUL 终止的非 NUL 字符序列。

当然,您可以通过 bash 管道自由地传递 NUL,并且没有什么能阻止您将 shell 变量分配给输出 NUL 字节的程序的输出。但是,根据 Posix(XSH 2.6.3“如果输出包含任何空字节,则行为未指定”),后果是“未指定的”。在 bash 中,NUL 被删除,除非您使用 bash 的 C 转义语法 ( ) 将 NUL 插入到字符串中$'\0',在这种情况下,NUL 将终止该值。

实际上,请考虑以下两种尝试将 NUL 插入stdin实用程序的方法之间的区别:

$ # Prefer printf to echo -n
$ printf $'foo\0bar' | wc -c
3
$ printf 'foo\0bar' | wc -c
7
$ # Bash extension which is better for strings which might contain %
$ printf %b 'foo\0bar' | wc -c
7
于 2013-10-07T15:54:39.503 回答
5

但是为什么 bash 不将$'\0'and$'\x0'转换为空字符呢?

因为空字符终止了一个字符串。

$ echo $'hey\0you'
hey
于 2013-10-07T14:54:48.417 回答
4

它是一个空字符,但这取决于您的意思。

空字符代表一个空字符串,这是你展开它时得到的。这是一个特例,我认为文档中暗示了这一点,但实际上并未说明。

在 C 中,二进制零'\0'终止一个字符串,它本身也代表一个空字符串。Bash 是用 C 语言编写的,所以它可能是从那开始的。

编辑:POSIX 在很多地方都提到了一个空字符串。在“基本定义”中,它将一个空字符串定义为:

3.146 空字符串(或空字符串)
第一个字节为空字节的字符串。

于 2013-10-07T14:48:34.167 回答