我对Bash的语法有一个理论上的问题 我在Linux Ubuntu 14.04中运行Bash 4.3.11(1)。
在官方GNU的网站上:Bash official web (GNU)
在Subection 9.3.1中。它说:
!串
请参阅当前位置之前的最新命令 在以字符串开头的历史列表中。
一般来说,从理论上讲,string
是在第一个空格或换行符之前结束的字符序列。
但是,在3.1.2。小节中描述引用时,我们可以在第3.1.2.2段中读到。以下是:
用单引号括起字符(''')保留字面值 引号内每个字符的值。
特别是,单引号内的空格不会用分隔的单词来表示字符串。
因此,像!'some text'
这样的表达式必须在Bash的历史列表中搜索从'some text'
开始的最新命令。
但是,当我在终端中写入时some
和text
之间的空白被破坏,因为显示以下错误消息:
bash:!' some:event not found
这种行为是shell实现中的一个错误,还是我不了解Bash的扩展规则?
答案 0 :(得分:2)
我不会将观察到的行为称为错误,因为除了观察到的bash shell本身的行为之外,没有规范历史扩展。但肯定的是,解析历史扩展表达式的精确机制没有很好的记录,并且有很多可能令人惊讶的极端情况。
bash联机帮助页确实说明历史扩展"是在读完整行之后立即执行的,在shell将其分解为单词之前" (重点补充),而bash手册提到历史扩展是由历史库提供的。这是解决奇怪问题的大多数历史扩展的根本原因:历史扩展在没有来自bash tokenizer的任何帮助的情况下对原始未解析的输入起作用,并且主要使用不是特定于bash的外部库来完成。由于对bash
输入进行标记化并非易事,因此在历史扩展期间使用的相对简单的解析规则只是对真正的bash解析的粗略近似并不奇怪。
例如,bash手册确实表明您可以通过反斜杠引用来阻止历史记录扩展字符(!)被识别。但是没有明确记录任何紧接在!之前的 \ 将禁止识别历史扩展,即使反斜杠本身用反斜杠引用也是如此。所以\\!word
中的!确实不导致以word
开头的上一个命令被替换。 (\\word
是执行命令 word
而不是别名 word
的常用方法,因此示例并非完全做作。)
可以在this answer中找到关于识别历史扩展字符的一些极端案例的更长时间讨论。
这个问题提出的问题略有不同,因为它是关于历史扩展解析的下一阶段。一旦确定特定字符是历史扩展字符,就必须解析"事件"以下;如bash手册所示,该事件可以采用多种形式,其中一种形式为!string
,代表最新的命令,以" string
"开始。
暗示只有在没有其他形式适用的情况下才会使用此表单,这意味着string
可能不会以数字开头或 - ,!,#或?。它也可能不是以空格或 = 开头(因为那些会抑制历史扩展),在某些情况下(或" (其中)可能会抑制历史扩展)。最后,它可能不会以 ^ , $ ,%或 * 开头,这将被解释为一个单词指示符(来自默认事件,这是上一个命令)。
bash manual未指定终止string
的内容。它在history
library manual中有半文档记录,其中提到历史搜索字符串(或者"事件"因为它在bash手册中调用)由空格终止,:,或历史配置变量history_search_delimiter_chars
中的任何字符。 (对于记录,当前bash
(v4.3)将该变量设置为";&()|<>"
。)
如前所述,在决定是否识别历史扩展字符时会考虑引用;事实证明,如果历史扩展发生在双引号字符串中,则结束双引号也被视为历史搜索分隔符。据我所知,这是将划分!string
的整个字符列表。
无论是bash还是历史文档都没有说明历史搜索分隔符可以通过引用变得非特殊,实际上这不会发生。一个开放的引用,无论是双引号还是单引号,甚至是反斜杠跟随!,都将被视为要搜索的string
的一部分,而不是特殊处理。
解析子字符串匹配历史记录扩展 - !?string?
- 完全不同。该字符串只能由?或换行符终止。 (正如bash手册所说,如果用换行符终止,则尾随?是可选的。)
一旦识别了历史扩展字符并且识别了历史搜索字符串,则可能需要将检索到的历史记录条目拆分为单词。再说一遍,bash手册对于角落的情况略显傲慢,当它说&#34;这条线以与Bash相同的方式被分成单词时,所以被引号包围的几个单词被认为是一个单词。&#34;
一个学究会以与Bash相同的方式观察到&#34;&#34;与Bash所说的完全相同&#34;并且实际上句子的第二部分是真实的:被引号括起来的几个单词被认为是一个单词,即使引号是没有真正匹配的报价。例如,行:
command "$(echo " foo bar ")"
历史库将视为由以下五个单词组成:
0. command
1. "$(echo "
2. foo
3. bar
4. ")"
虽然bash解析会有很大的不同。相比之下,bash和历史库同意解析
command "$(echo ' foo bar ')"
两个字。