操作历史

简介

Bash 会保留用户的操作历史,即用户输入的每一条命令都会记录,默认是保存最近的500条命令。有了操作历史以后,就可以使用方向键的,快速浏览上一条和下一条命令。

退出当前 Shell 的时候,Bash 会将用户在当前 Shell 的操作历史写入~/.bash_history文件,该文件默认储存500个操作。

环境变量HISTFILE总是指向这个文件。

  1. $ echo $HISTFILE
  2. /home/me/.bash_history

history 命令

history命令会输出.bash_history文件的全部内容,即输出操作历史。

  1. $ history
  2. ...
  3. 498 echo Goodbye
  4. 499 ls ~
  5. 500 cd

用户可以使用这个命令,查看最近的操作。相比直接读取.bash_history文件,它的优势在于所有命令之前加上了行号。最近的操作在最后面,行号最大。

如果想搜索某个以前执行的命令,可以配合grep命令搜索操作历史。

  1. $ history | grep /usr/bin

上面命令返回.bash_history文件里面,那些包含/usr/bin的命令。

history命令的-c参数可以清除操作历史,即清空.bash_history文件。

  1. $ history -c

环境变量

HISTTIMEFORMAT

通过定制环境变量HISTTIMEFORMAThistory的输出结果还可以显示每个操作的时间。

  1. $ export HISTTIMEFORMAT='%F %T '
  2. $ history
  3. 1 2013-06-09 10:40:12 cat /etc/issue
  4. 2 2013-06-09 10:40:12 clear

上面代码中,%F相当于%Y - %m - %d(年-月-日),%T相当于%H : %M : %S(时:分:秒)。

只要设置HISTTIMEFORMAT这个环境变量,就会在.bash_history文件保存命令的执行时间戳。如果不设置,就不会保存时间戳。

HISTSIZE

环境变量HISTSIZE设置保存历史操作的数量。

  1. $ export HISTSIZE=10000

上面命令设置保存过去10000条操作历史。

如果不希望保存本次操作的历史,可以设置HISTSIZE等于0。

  1. export HISTSIZE=0

如果HISTSIZE=0写入用户主目录的~/.bashrc文件,那么就不会保留该用户的操作历史。如果写入/etc/profile,整个系统都不会保留操作历史。

HISTIGNORE

环境变量HISTIGNORE可以设置哪些命令不写入操作历史。

  1. export HISTIGNORE='pwd:ls:exit'

上面示例设置,pwdlsexit这三个命令不写入操作历史。

Ctrl + r

输入命令时,按下Ctrl + r快捷键,就可以搜索操作历史,选择以前执行过的命令。

Ctrl + r相当于打开一个.bash_history文件的搜索接口,直接键入命令的开头部分,Shell 就会自动在该文件中反向查询(即先查询最近的命令),显示最近一条匹配的结果,这时按下回车键,就会执行那条命令。

! 命令

! + 行号

操作历史的每一条记录都有行号。知道了命令的行号以后,可以用感叹号 + 行号执行该命令。如果想要执行.bash_history里面的第8条命令,可以像下面这样操作。

  1. $ !8

!- 数字

如果想执行本次 Shell 对话中倒数的命令,比如执行倒数第3条命令,就可以输入!-3

  1. $ touch a.txt
  2. $ touch b.txt
  3. $ touch c.txt
  4. $ !-3
  5. touch a.txt

上面示例中,!-3返回倒数第3条命令,即touch a.txt

它跟! + 行号的主要区别是,后者是在.bash_history文件中从头开始计算行数,而!- 数字是从底部开始向上计算行数。

!!

!!命令返回上一条命令。如果需要重复执行某一条命令,就可以不断键入!!,这样非常方便。它等同于!-1

  1. $ echo hello
  2. hello
  3. $ !!
  4. echo hello
  5. hello

上面示例中,!!会返回并执行上一条命令echo hello

有时候,我们使用某条命令,系统报错没有权限,这时就可以使用sudo !!

  1. # 报错,没有执行权限
  2. $ yum update
  3. $ sudo !!
  4. sudo yum update

上面示例中,sudo !!返回sudo yum update,从而就可以正确执行了。

! + 搜索词

感叹号 + 搜索词可以快速执行匹配的命令。

  1. $ echo Hello World
  2. Hello World
  3. $ echo Goodbye
  4. Goodbye
  5. $ !e
  6. echo Goodbye
  7. Goodbye

上面例子中,!e表示找出操作历史之中,最近的那一条以e开头的命令并执行。Bash 会先输出那一条命令echo Goodbye,然后直接执行。

同理,!echo也会执行最近一条以echo开头的命令。

  1. $ !echo
  2. echo Goodbye
  3. Goodbye
  4. $ !echo H
  5. echo Goodbye H
  6. Goodbye H
  7. $ !echo H G
  8. echo Goodbye H G
  9. Goodbye H G

注意,感叹号 + 搜索词语法只会匹配命令,不会匹配参数。所以!echo H不会执行echo Hello World,而是会执行echo Goodbye,并把参数H附加在这条命令之后。同理,!echo H G也是等同于echo Goodbye命令之后附加H G

由于感叹号 + 搜索词会扩展成以前执行过的命令,所以含有!的字符串放在双引号里面,必须非常小心,如果它后面有非空格的字符,就很有可能报错。

  1. $ echo "I say:\"hello!\""
  2. bash: !\: event not found

上面的命令会报错,原因是感叹号后面是一个反斜杠,Bash 会尝试寻找,以前是否执行过反斜杠开头的命令,一旦找不到就会报错。解决方法就是在感叹号前面,也加上反斜杠。

  1. $ echo "I say:\"hello\!\""
  2. I say:"hello\!"

!? + 搜索词

!? + 搜索词可以搜索命令的任意部分,包括参数部分。它跟! + 搜索词的主要区别是,后者是从行首开始匹配。

  1. $ cat hello.txt
  2. Hello world ..!
  3. $ !?hello.txt
  4. cat hello.txt
  5. Hello world ..!

上面示例中,!?hello.txt会返回最近一条包括hello.txt的命令。

!$,!*

!$代表上一个命令的最后一个参数,它的另一种写法是$_

!*代表上一个命令的所有参数,即除了命令以外的所有部分。

  1. $ cp a.txt b.txt
  2. $ echo !$
  3. b.txt
  4. $ cp a.txt b.txt
  5. $ echo !*
  6. a.txt b.txt

上面示例中,!$代表上一个命令的最后一个参数(b.txt),!*代表上一个命令的所有参数(a.txt b.txt)。

如果想匹配上一个命令的某个指定位置的参数,使用!:n

  1. $ ls a.txt b.txt c.txt
  2. $ echo !:2
  3. b.txt

上面示例中,!:2返回上一条命令的第二个参数(b.txt)。

这种写法的!:$,代表上一个命令的最后一个参数。事实上,!$就是!:$的简写形式。

  1. $ ls a.txt b.txt c.txt
  2. $ echo !:$
  3. echo c.txt
  4. c.txt

上面示例中,!:$代表上一条命令的最后一个参数(c.txt)。

如果想匹配更久以前的命令的参数,可以使用!<命令>:n(指定位置的参数)和!<命令>:$(最后一个参数)。

  1. $ ls !mkdir:$

上面示例中,!mkdir:$会返回前面最后一条mkdir命令的最后一个参数。

  1. $ ls !mk:2

上面示例中,!mk:2会返回前面最后一条以mk开头的命令的第二个参数。

!:p

如果只是想输出上一条命令,而不是执行它,可以使用!:p

  1. $ echo hello
  2. $ !:p
  3. echo hello

上面示例中,!:p只会输出echo hello,而不会执行这条命令。

如果想输出最近一条匹配的命令,而不执行它,可以使用!<命令>:p

  1. $ !su:p

上面示例中,!su:p会输出前面最近一条以su开头的命令,而不执行它。

^string1^string2

^string1^string2用来执行最近一条包含string1的命令,将其替换成string2

  1. $ rm /var/log/httpd/error.log
  2. $ ^error^access
  3. rm /var/log/httpd/access.log

上面示例中,^error^access将最近一条含有error的命令里面的error,替换成access

histverify 参数

上面的那些快捷命令(比如!!命令),都是找到匹配的命令后,直接执行。如果希望增加一个确认步骤,先输出是什么命令,让用户确认后再执行,可以打开 Shell 的histverify选项。

  1. $ shopt -s histverify

打开histverify这个选项后,使用!快捷键所返回的命令,就会先输出,等到用户按下回车键后再执行。

快捷键

下面是其他一些与操作历史相关的快捷键。

  • Ctrl + p:显示上一个命令,与向上箭头效果相同(previous)。
  • Ctrl + n:显示下一个命令,与向下箭头效果相同(next)。
  • Alt + <:显示第一个命令。
  • Alt + >:显示最后一个命令,即当前的命令。
  • Ctrl + o:执行历史文件里面的当前条目,并自动显示下一条命令。这对重复执行某个序列的命令很有帮助。

参考链接