Linux 历史扩展中的单引号(bash)

Linux 历史扩展中的单引号(bash),linux,string,bash,syntax,Linux,String,Bash,Syntax,我有一个关于Bash语法的理论问题。 我正在Linux Ubuntu 14.04中运行Bash4.3.11(1) 在GNU官方网站: 在第9.3.1小节中。它说: !!串 参考当前位置之前的最新命令 在历史记录列表中,以字符串开头 一般来说,string在语法上是以第一个空格或换行符结尾的字符序列 然而,当描述第3.1.2小节中的引用时,我们可以阅读第3.1.2.2段。以下内容: 在单引号(“”)中包含字符将保留文字 引号中每个字符的值 特别是,单引号内的空格并没有将字符串分隔开 所以,像!”这

我有一个关于Bash语法的理论问题。
我正在Linux Ubuntu 14.04中运行Bash4.3.11(1)

在GNU官方网站:
在第9.3.1小节中。它说:

!!串

参考当前位置之前的最新命令 在历史记录列表中,以字符串开头

一般来说,
string
在语法上是以第一个空格或换行符结尾的字符序列

然而,当描述第3.1.2小节中的引用时,我们可以阅读第3.1.2.2段。以下内容:

在单引号(“”)中包含字符将保留文字 引号中每个字符的值

特别是,单引号内的空格并没有将字符串分隔开

所以,像
!”这样的表达式某些文本“
必须在Bash的历史记录列表中搜索以“
”某些文本“
开头的最新命令

<0>但是,当我在我的终端上写代码时,<代码> > < <代码> >代码>文本< /代码>之间的空白被打破,因为下面的错误信息显示:

bash:!'某些:未找到事件


这种行为是shell实现中的一个bug,还是我不理解这个示例中Bash的扩展规则

我不会将观察到的行为称为bug,因为除了bash shell本身的观察行为之外,没有任何关于历史扩展的规范。但确实如此,解析历史扩展表达式的精确机制没有很好的文档记录,并且有许多可能令人惊讶的情况

bash手册页确实声明历史扩展“在读取完整的行之后,在shell将其分解为单词之前立即执行”(强调添加),而bash手册提到历史扩展是由历史库提供的。这是大多数历史扩展解析异常的根本原因:历史扩展在未经解析的原始输入上工作,而不需要bash标记器的任何帮助,并且主要是通过非bash特定的外部库完成的。由于标记化
bash
输入是非常重要的,所以在历史扩展期间使用的相对简单的解析规则只是实际bash解析的粗略近似值,这并不奇怪

例如,bash手册确实指出,可以通过反斜杠引用历史扩展字符(!)来防止识别它。但是没有明确的文件证明,任何\都直接位于!将禁止识别历史扩展,即使反斜杠本身引用了反斜杠。所以!在
\\!word
不会导致替换以word开头的上一个命令。(
\\word
是执行命令
word
而不是别名
word
的常用方法,因此该示例并非完全是人为的。)

有关识别历史扩展字符的一些角落案例的详细讨论,请参见

这个问题提出的问题略有不同,因为它是关于历史扩展解析的下一个阶段。一旦确定某个特定字符是历史扩展字符,就需要解析后面的“事件”;如bash手册所示,事件可以有多种形式,其中之一是
!字符串,表示以“
string
”开头的最新命令

这意味着只有在没有其他形式适用的情况下才会使用此形式,这意味着
string
不能以数字或-,!,#开头或它也可能不会以空格或=(因为它们会禁止历史扩展)开头,在某些情况下(或)(可能会禁止历史扩展)。最后,它可能不会以^、$、%或*开头,这将被解释为单词指示符(来自默认事件,即前一个命令)

没有指定终止
字符串的内容。
中有半文档记录,其中提到历史搜索字符串(或在bash手册中称为“事件”)由空格、:、或历史配置变量
历史搜索定界符\u字符
中的任何字符终止。(对于记录,
bash
当前(v4.3)将该变量设置为
“;&()|”

如前所述,在决定是否识别历史扩展字符时,会考虑引号;事实证明,如果历史扩展发生在双引号字符串中,则结束双引号也会被视为历史搜索分隔符字符。据我所知,这就是cha的整个列表分隔符,用于分隔
!string

bash和history文档中都没有说明历史搜索分隔符字符可以通过引号变为非特殊字符,事实上这不会发生。打开的引号,无论是双引号还是单引号,甚至是!后面的反斜杠,都将被视为要搜索的
字符串的一部分,而不包含任何字符特殊处理

子字符串匹配历史扩展的解析--
!?字符串?
--完全不同。该字符串只能由?或换行符终止。(正如bash手册所述,如果由换行符终止,则尾部?是可选的。)

一旦识别出历史扩展字符并识别出历史搜索字符串,则可能需要将检索到的历史条目拆分为单词。同样,bash手册对角案例的描述有些轻率,因为它说“该行以与BASH所做的相同的方式被分解成单词,因此考虑了由引文包围的几个单词。
command "$(echo " foo bar ")"
0. command
1. "$(echo "
2. foo
3. bar
4. ")"
command "$(echo ' foo bar ')"