感叹号:bash 的历史扩展功能

Bash 的历史扩展(History Expansion)又被称为 Bang(!) 命令,历史扩展是 bash 将历史命令转换到可执行命令的过程。Bash 下的 History 库提供了一个与 csh 下历史扩展类似的历史扩展功能。历史扩展中操作历史命令一般有两个部分:

  1. 首先要从历史命令中找出相对应的命令,被选择到的命令我们称作为Event(条目),比如Bang Bang(!!),就是选择最后一条命令;
  2. 选择选定行的部分或全部文本以包含到当前行中。要操作的条目(Event)Bash将其拆分成了Words(词),命令中的Words是靠空格来分割的,我们就可以使用修饰符(Modifiers)来调整Words以符合我们的要求。注意:Words并不是英文单词,而是一个字符序列而已。

先来看两个命令,你知道第二个命令是什么意思么?


  1. cat /tmp/cat.cat.txt
  2. !:0 !*:gs/cat./echo.

条目标志符(Event Designators)

条目标志符是一个到历史列表内一个命令行实体的引用,除非是绝对引用,不然条目的引用是相对历史列表中当前位置的。

条目标志符 条目标志符说明
! 开始一个历史替换,除非后面紧跟的是空格,制表符,行结束符,"=","("(当使用内建命令shopt开启了extglob的shell选项)。
!n 重复历史中编号为n的命令——历史编号可以参看history命令.
!-n 执行之前的第n条命令,执行上一条命令可以使用!!或者!-1,执行之前第三条命令:!-3,倒推的列表是history
!! 执行上一条命令,和Ctrl-P,!-1的作用一样。
!string 执行最近的以string字串开头的命令。这个命令的意思是重复以!后字串开头的最后一条命令,比如:!ca将重复以字符ca开头的最后一条命令,如cat ReadMe,(假设最近一条ca开头是这个命令,并且ReadMe后紧跟换行符)
!?string[?] 在历史列表中以当前位置开始向后查找(往回搜索)包含string字符串的最近一条命令,如果要查找的string字符串后面紧跟换行符,则string后面的这个问号可以省略。例如:!?Read?还是会匹配cat ReadMe。(同上的环境),如果后面是换行符如:!?ReadMe,则不用输入结尾的[?]。
^a^b 快速替换,把上一条命令中的a替换成b,并执行替换后的命令。^a^b^类似。注意:这里只是替换一个找到的实例,相当于:!!:s/a/b
^abc 删除上一条命令中的abc。
!# 引用目前输入的所有字串,如:more a !#;这个最终的命令是more a more a

词标志符(Word Designators)

词标志符被用来在条目里面选择需要的词。一般用":"分隔条目指示符和词指示符。当词指示符是以"^","$","*","-","%"开头时,也可能会省略":"。词是从一行的行首开始,第一个词编号为0.插入到当前行中时,这些词使用单个空格分隔。

词标志符 词标志符说明
0 第0个词,在很多应用程序中,这就是命令本身。
n 第n个词
^ 第一个参数;也就是第一个词。
$ 最后一个参数。
% 最近"?string?"匹配的词。
x-y 词的范围:如果是'0-y'可以简写成'-y'.
* 除了第0个以外的所有词,这个和'1-$'同义,如果条目中只有一个词,使用'*'也不会返回错误,仅是返回一个空字符串而已。
x* 'x-$'的简写
x- 和x*类似,都是'x-$'的简写,不过需要注意,这个写法是忽略最后一个词的。

需要注意的是,在Bash下使用词指示符的时候,可以没有条目指示符,如果没有使用条目指示符,则会把前一条命令作为词指示符的操作条目。

修饰符(Modifiers)

在可选的词指示符之后,你可以添加下面修饰符中的一个或多个,每个修饰符以':'开头。

修饰符 修饰符说明
h 去掉路径名的尾部,只保留头部。只移除最后一个'/'后面的内容,可以理解成是路径名的父目录。
t 去掉路径名部件中除尾部之外的所有内容。只保留最后一个'/'后的内容。
r 去掉尾部这样格式".suffix"的一个结尾后缀,保留基本名称。只删除最后一个点'.'后的内容。
e 仅保留后缀。仅保留最后一个点'.'及点后的内容。
p 打印新的命令但不执行。
q 引用替换的词,防止进一步替换。(译注,原文:Quote the substituted words, escapin further substitutions.——Mitchell Chu)。这个引用会直接对引用的命令加上单引号,防止进一步替换。开始这句不知道怎么翻译。后来Mitchell发现自己的这个翻译并没有错误,因为我们引用的词可能是个变量,这时候如果没有引号,就会引起进一步的替换,而是用此参数就能达到防止这种情况的发生。
x 这个和q一样,是引用替换的词,但是这个与q不同的地方在于,q是整体引用,而这个是会将替换的词使用空格,制表符,换行符来分割成一个个的词。
s/old/new/ 把条目行中找到的第一个old位置的内容替换成new位置的内容,'/'这个分隔符位置可以使用任何其他字符作为分隔符。如果要在old或new位置使用分隔符,需要使用反斜杆'\'来转义。如果'&'这个字符出现在new位置,将会被替换成old位置的内容,如果要使用'&'请用'\'转义。最后一个分隔符如果是整行的最后一个字符,则可以省略。
& 重复上次替换。这个是引用最后一次的s/old/new/内容。
g 见下,与a相同
a 使替换在整个条目中进行,和's'一起使用,例如:!!:gs/old/new/,或者和'&'一起使用。
G
对条目中的每一个词都执行一次其后的's'修饰符。这个方法在Bash 4.1.2下测试并不靠谱。


  1. test $eee /tmp/test.log
  2. echo !test:Gs/t/a/;
  3. ## 这个返回的test被替换两次
  4. ## 但后面的参数仅替换一次

因此Mitchell在想,是不是仅对参数执行一次,而对命令(第0个词)进行全局替换。但另外一个测试,反驳了这个观点:


  1. aaaaaaaaaaa $aaaaaaa /tmp/aaaaaaaaaaaaa.log
  2. echo !aaaa:q:Gs/a/t/
  3. ## 此时,最多的替换出现在!:0,两次!

但多次测试结果来看,第零个词汇被替换最多两次,其他只替换一次。具体原因暂时未知!

原文发布时间为:2015-06-19

本文来自合作伙伴“Linux中国”

时间: 2024-09-12 06:11:33

感叹号:bash 的历史扩展功能的相关文章

Bash中的特殊字符

Bash中,用在脚本和其他地方的字符叫做特殊字符.下面依次举例介绍每个字符的用途. # 行首以#(#!是个例外)开头是注释. # This line is a comment. 注释也可以放在于本行命令的后边. echo "A comment will follow." # 注释在这里. 命令是不能放在同一行上注释的后边的.因为没有办法把注释结束掉,好让同一行上后边的"代码生效",只能够另起一行来使用下一个命令. 在echo中转义的#是不能作为注释的,同样也可以出现

UNIX 高手的 20 个习惯[转]

使用 mkdir 的 -p 选项并在单个命令中创建所有父目录及其子目录要容易得多.但是即使对于知道此选项的管理员,他们在命令行上创建子目录时也仍然束缚于逐步创建每级子目录.花时间有意识地养成这个好习惯是值得的. 清单 2. 好习惯 1 的示例:使用一个命令来定义目录树~ $ mkdir -p tmp/a/b/c您可以使用此选项来创建整个复杂的目录树(在脚本中使用是非常理想的),而不只是创建简单的层次结构.清单 3. 好习惯 1 的另一个示例:使用一个命令来定义复杂的目录树~ $ mkdir -p

命令行艺术

curl -s 'https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/README.md' | egrep -o '`\w+`' | tr -d '`' | cowsay -W50 流畅地使用命令行是一个常被忽略的技能,或被认为是神秘的奥义.但是,它会以明显而微妙的方式改善你作为工程师的灵活度和生产力.这是我在 Linux 上工作时发现的有用的命令行使用小窍门和笔记的精粹.有些小窍门是很基础的,而有些是相当

LINUX安全加固

  一. 账户安全 1.1 锁定系统中多余的自建帐号 检查方法: 执行命令 #cat /etc/passwd #cat /etc/shadow 查看账户.口令文件,与系统管理员确认不必要的账号.对于一些保留的系统伪帐户如:bin, sys,adm,uucp,lp, nuucp,hpdb, www, daemon等可根据需要锁定登陆. 备份方法: #cp -p /etc/passwd /etc/passwd_bak #cp -p /etc/shadow /etc/shadow_bak 加固方法:

LINUX 101: 让你的 SHELL 更强大

在我们的关于 shell 基础的指导下, 得到一个更灵活,功能更强大且多彩的命令行界面 为何要这样做? 使得在 shell 提示符下过得更轻松,高效 在失去连接后恢复先前的会话 Stop pushing around that fiddly rodent! bash1 这是我的命令行提示符的设置.对于这个小的终端窗口来说,这或许有些长.但你可以根据你的喜好来调整它. 作为一个 Linux 用户, 你可能熟悉 shell (又名为命令行). 或许你需要时不时的打开终端来完成那些不能在 GUI 下处

《C程序设计新思维》一1.7 通过本地文档来编译C程序

1.7 通过本地文档来编译C程序 到此,你应该已经看出编译过程的套路了. 1. 设定一个表述编译器选项的变量. 2. 设定一个表述连接器选项的变量,包括为你用的所有库配置的-l选项. 3. 用make命令或者你的IDE的操作来把这些变量转换为完整的编译和连接命令. 本章的余下部分将把以上步骤做最后一次,并采取一种非常简短的设置:仅仅用shell.如果你思维敏捷,可以通过摘录一些语句段落到解析器上来学习脚本,你也将可以同样地把C代码贴在你的命令行上.**1.7.1 在命令行里包含头文件**gcc和

Bash之命令历史的存储和记录

//ps: 这篇文章系bash的history相关,多了解点儿也好!不同session的history操作同步问题 在Bash中我们可以使用 history 命令回顾,修改和重用之前使用过的历史命令.去掉信号机制.去掉作业控制.去掉各种参数调用,今天我们只看下Bash中如何记录命令,如何存储历史命令. 在 Bash 的源代码中,history 命令的定义代码为 builtins/history.def , builtins 目录下存放的是内部命令的源代码,每个内部命令是一个def文件,如hist

为你在 Bash 历史中执行过的每一项命令设置时间和日期

在默认情况下,所有通过 Bash 在命令行中执行过的命令都被存储在历史缓存区或者一个叫做~/.bash_history 的文件里.这意味着系统管理员可以看到系统上用户执行过的命令清单,或者用户可以通过像 history 命令这样的选项来看他或她自己的命令历史. $ history Linux 历史命令 从上面 history 命令的输出可知,命令被执行的日期和时间并没有显示出来.基本上所有的 Linux 发行版的默认设置都是这样的. 在这篇文章里,我们将解释当在 Bash 中执行 history

Bash 中的特殊字符大全

Linux下无论如何都是要用到shell命令的,在Shell的实际使用中,有编程经验的很容易上手,但稍微有难度的是shell里面的那些个符号,各种特殊的符号在我们编写Shell脚本的时候如果能够用的好,往往能给我们起到事半功倍的效果,为此,特地将Shell里面的一些符号说明罗列成对照表的形式,以便快速的查找.看看你知道下表中的哪些Shell符号呢? Shell符号及各种解释对照表: Shell符号 使用方法及说明 # 注释符号(Hashmark[Comments]) 1.在shell文件的行首,