9.3 历史扩展(History Expansion)
历史库提供历史扩展特性,其类似于由 csh 提供的历史扩展。该章节所描述的是用于操纵历史信息的语法。
历史扩展将历史列表中的单词引入到输入流中,可以很容易地重复命令,可将上一命令的参数插入到当前输入行,可快速地在上一命令中修复错误。
在读取了一整行之后,和在 shell 将其分割为单词之前立即执行历史扩展。并且历史扩展是在每行上单独执行。Bash 会试图告知历史扩展函数,之前行中出现的引号一直处于有效状态。
历史扩展分为两部分。第一部分用来确定在替换时应该使用历史列表中的哪一行。第二部分用来选择该行的哪一部分应该放到当前行中。从历史中选择行被称为事件(event),此后用于操作的行的一部分被称为单词(words)。多种多样的修饰符(modifier)可用于操纵已选择的单词。将行分割为单词的方式与 Bash 分割单词的方式相同,因此由引号括起来的多个单词会被认为是一个单词。历史扩展是在出现了历史扩展字符开始的,其默认字符为“!”。
历史扩展执行类似 shell 的引用约定:一个反斜线可用于下一字符的特殊处理;单引号括起来一个连续的字符序列,并可用于阻止历史扩展;双引号内的字符可以进行历史扩展,因为反斜线能转义历史扩展字符,但是不能转义单引号,因为其在双引号内不会被特殊对待。
当使用 shell 时,只有反斜线“\”和单引号“‘”可用于转义历史扩展字符,但如果在双引字符串中历史扩展字符后紧跟着双引号结束字符,其也会被视为引用的。
一些由内建命令 shopt(参见 4.3.2《内建命令 shopt》)可设置的 shell 选项可用于调整历史扩展行为。如果开启了 shell 选项“histverify”,并且正在使用 Readline,历史替换不会立即传递给 shell 分析器。而是将扩展行重新加载到 Readline 编辑缓冲中用于进一步的修改。如果正在使用 Readline,并且开启了 shell 选项“histreedit”,失败的历史扩展将会重新载入到 Readline 编辑缓冲中进行修正。内建命令 history 的“-p”选项可用于查看历史扩展将要做什么,这可在使用该扩展之前进行结果的测试。内建命令 history 的“-s”选项可用于将给出的命令添加到历史列表末尾而不实际执行它,因此其可用于后继的再调用。这与 Readline 结合使用是非常有用的。
shell 允许历史扩展机制使用变量“histchars”进行多字符控制,关于该变量的说明可参见 5.2《Bash 变量》。当写入历史文件时,shell 使用历史注释字符来标记历史时间截。
9.3.1 事件标志符(Event Designators)
一个事件标志符是会引用到历史列表中的一个命令行条目上。除非是绝对引用,否则事件相对引用到历史列表中的当前位置。
| 事件标志符 | 说明 |
|---|---|
| ! | 开始一个历史替换,除非其后跟随着空格“space”、制表符“tab”、行尾、等号“=”或前半个小括号“(”(当使用了内建命令 shopt 开启了 shell 选项“extglob”)。 |
| !n | 引用到第 n 行命令。 |
| !-n | 向后引用到第 n 行命令。 |
| !! | 引用到上一次命令。等同于“!-1”。 |
| !string | 引用到历史列表中据当前位置上一次以“string”开头的命令。 |
| !?string[?] | 引用到历史列表中据当前位置之前最近一次以该“string”开头的命令。如果“string”后面紧跟着换行,尾部的问号是可以省略的。如果缺省“string”,则使用上一次搜索所使用的“string”;如果没有进行过上一次“string”的搜索,则会报错。 |
| ^string1^string2^ | 快速替换。重复最后一次命令,将其中的“string1”替换为“string2”。等同于“!!:s^sting1^string2^”。 |
| !# | 到目前为止输入的整个命令行。 |
9.3.2 单词标志符(Word Designators)
单词标志符用于从事件中选择想要的单词。冒号“:”用于在单词标志符中分隔事件规格说明。如果单词标志符由“^”、“$”、“*”、“-”或“%”开头,其可以省略冒号。单词是从行首开始编号,第一个单词标识为 0(零)。插入到当前行的单词由单空格分隔。例如:
!!
选定上一次命令。当你键入这两个叹号,会完全重复上一次运行的命令。
!!:$
选定上一次命令中的最后一个参数。其可以简写为“!$”。
!fi:2
选定最近一次以字母“fi”开头的命令的第二个参数。
下面是单词标志符的说明:
| 单词标志符 | 说明 |
|---|---|
| 0(零) | 第 0 个单词。在很多应用程序中,其代表命令单词。 |
| n | 第 n 个单词。 |
| ^ | 第一个参数,即第 1 个单词。 |
| $ | 最后一个参数。 |
| % | 最近一次“?string?”搜索匹配到的第一个单词,如果单词开头部分的字符是搜索字符串的话。 |
| x-y | 一个范围内的单词。“-y”是“0-y”的缩写。 |
| * | 不包括第 0 位的所有的单词。其是“1-$”的代名词。如果在事件中只有一个单词,使用“*”是不会报错的;在这种情况下会返回一个空字符串。 |
| x* | “x-$”的缩写。 |
| x- | 像“x*”一样是“x-$”的缩写,但会省略最后一个单词。如果缺省“x”,其默认为“0”。 |
如果给出单词标志符时不给出事件规格说明,则会使用上一命令作为事件。
9.3.3 修饰符(Modifiers)
在可选的单词标志符之后,你可以添加一个或多个后继修饰符序列,每个修饰符序列之前要带有冒号“:”。这些修改或编辑的是从历史事件中选择出来的一个或多个单词。
| 修饰符 | 说明 |
|---|---|
| h | 移除路径名中的末尾部分,只保留路径的前面部分。 |
| t | 移除路径名中前面部分,只保留末尾部分。 |
| r | 移除以“.suffix”形式末尾部分的后缀,保留基本名。 |
| e | 移除除末尾后缀的其它所有部分。 |
| p | 打印新命令但不执行。 |
| q | 将已替换单词引起来,转义下一步的替换。 |
| x | 将已替换单词引起来,就像使用“q”一样。但在空格、制表符和换行处分割单词。修饰符“q”和“x”相互排斥;后出现的有效。 |
| s/old/new/ | 在事件行中第一次出现的“old”替换为“new”。任意字符可用作代替“/”的分隔符。可以使用单个反斜线将在“old”和“new”中出现的分隔符引用为其本身字符。如果与符号“&”出现在“new”中,其可替换为“old”字符串。单反斜线也可引用“&”符号。如果“old”为空,其设置为上一次替换的“old”,或如果之前没有过历史替换,则是“!?string[?]”搜索的最后一个“string”。如果“new”为空,每一个匹配到“old”的将被删除。如果最后一个分隔符是输入行中的最后一个字符,则可以省略该分隔符。 |
| & | 重复上一次替换。 |
| g a | 会将变更应用于整个事件行。一般与“s”一起使用,例如“gs/old/new/”,或与“&”一起使用。 |
| G | 在事件中对每一个单词应用后面的“s”或“&”修饰符。 |