|
| 1 | +# 操作历史 |
| 2 | + |
| 3 | +## 简介 |
| 4 | + |
| 5 | +Bash 会保留用户的操作历史,即用户输入的每一条命令都会记录,默认是保存最近的500条命令。有了操作历史以后,就可以使用方向键的`↑`和`↓`,快速浏览上一条和下一条命令。 |
| 6 | + |
| 7 | +退出当前 Shell 的时候,Bash 会将用户在当前 Shell 的操作历史写入`~/.bash_history`文件,该文件默认储存500个操作。 |
| 8 | + |
| 9 | +环境变量`HISTFILE`总是指向这个文件。 |
| 10 | + |
| 11 | +```bash |
| 12 | +$ echo $HISTFILE |
| 13 | +/home/me/.bash_history |
| 14 | +``` |
| 15 | + |
| 16 | +## history 命令 |
| 17 | + |
| 18 | +`history`命令会输出`.bash_history`文件的全部内容,即输出操作历史。 |
| 19 | + |
| 20 | +```bash |
| 21 | +$ history |
| 22 | +... |
| 23 | +498 echo Goodbye |
| 24 | +499 ls ~ |
| 25 | +500 cd |
| 26 | +``` |
| 27 | + |
| 28 | +用户可以使用这个命令,查看最近的操作。相比直接读取`.bash_history`文件,它的优势在于所有命令之前加上了行号。最近的操作在最后面,行号最大。 |
| 29 | + |
| 30 | +如果想搜索某个以前执行的命令,可以配合`grep`命令搜索操作历史。 |
| 31 | + |
| 32 | +```bash |
| 33 | +$ history | grep /usr/bin |
| 34 | +``` |
| 35 | + |
| 36 | +上面命令返回`.bash_history`文件里面,那些包含`/usr/bin`的命令。 |
| 37 | + |
| 38 | +`history`命令的`-c`参数可以清除操作历史,即清空`.bash_history`文件。 |
| 39 | + |
| 40 | +```bash |
| 41 | +$ history -c |
| 42 | +``` |
| 43 | + |
| 44 | +## 环境变量 |
| 45 | + |
| 46 | +### HISTTIMEFORMAT |
| 47 | + |
| 48 | +通过定制环境变量`HISTTIMEFORMAT`,`history`的输出结果还可以显示每个操作的时间。 |
| 49 | + |
| 50 | +```bash |
| 51 | +$ export HISTTIMEFORMAT='%F %T ' |
| 52 | +$ history |
| 53 | +1 2013-06-09 10:40:12 cat /etc/issue |
| 54 | +2 2013-06-09 10:40:12 clear |
| 55 | +``` |
| 56 | + |
| 57 | +上面代码中,`%F`相当于`%Y - %m - %d`(年-月-日),`%T`相当于` %H : %M : %S`(时:分:秒)。 |
| 58 | + |
| 59 | +只要设置`HISTTIMEFORMAT`这个环境变量,就会在`.bash_history`文件保存命令的执行时间戳。如果不设置,就不会保存时间戳。 |
| 60 | + |
| 61 | +### HISTSIZE |
| 62 | + |
| 63 | +环境变量`HISTSIZE`设置保存历史操作的数量。 |
| 64 | + |
| 65 | +```bash |
| 66 | +$ export HISTSIZE=10000 |
| 67 | +``` |
| 68 | + |
| 69 | +上面命令设置保存过去10000条操作历史。 |
| 70 | + |
| 71 | +如果不希望保存本次操作的历史,可以设置`HISTSIZE`等于0。 |
| 72 | + |
| 73 | +```bash |
| 74 | +export HISTSIZE=0 |
| 75 | +``` |
| 76 | + |
| 77 | +如果`HISTSIZE=0`写入用户主目录的`~/.bashrc`文件,那么就不会保留该用户的操作历史。如果写入`/etc/profile`,整个系统都不会保留操作历史。 |
| 78 | + |
| 79 | +### HISTIGNORE |
| 80 | + |
| 81 | +环境变量`HISTIGNORE`可以设置哪些命令不写入操作历史。 |
| 82 | + |
| 83 | +```bash |
| 84 | +export HISTIGNORE='pwd:ls:exit' |
| 85 | +``` |
| 86 | + |
| 87 | +上面示例设置,`pwd`、`ls`、`exit`这三个命令不写入操作历史。 |
| 88 | + |
| 89 | +## Ctrl + r |
| 90 | + |
| 91 | +输入命令时,按下`Ctrl + r`快捷键,就可以搜索操作历史,选择以前执行过的命令。 |
| 92 | + |
| 93 | +`Ctrl + r`相当于打开一个`.bash_history`文件的搜索接口,直接键入命令的开头部分,Shell 就会自动在该文件中反向查询(即先查询最近的命令),显示最近一条匹配的结果,这时按下回车键,就会执行那条命令。 |
| 94 | + |
| 95 | +## ! 命令 |
| 96 | + |
| 97 | +### ! + 行号 |
| 98 | + |
| 99 | +操作历史的每一条记录都有行号。知道了命令的行号以后,可以用`感叹号 + 行号`执行该命令。如果想要执行`.bash_history`里面的第8条命令,可以像下面这样操作。 |
| 100 | + |
| 101 | +```bash |
| 102 | +$ !8 |
| 103 | +``` |
| 104 | + |
| 105 | +### !- 数字 |
| 106 | + |
| 107 | +如果想执行本次 Shell 对话中倒数的命令,比如执行倒数第3条命令,就可以输入`!-3`。 |
| 108 | + |
| 109 | +```bash |
| 110 | +$ touch a.txt |
| 111 | +$ touch b.txt |
| 112 | +$ touch c.txt |
| 113 | + |
| 114 | +$ !-3 |
| 115 | +touch a.txt |
| 116 | +``` |
| 117 | + |
| 118 | +上面示例中,`!-3`返回倒数第3条命令,即`touch a.txt`。 |
| 119 | + |
| 120 | +它跟`! + 行号`的主要区别是,后者是在`.bash_history`文件中从头开始计算行数,而`!- 数字`是从底部开始向上计算行数。 |
| 121 | + |
| 122 | +### !! |
| 123 | + |
| 124 | +`!!`命令返回上一条命令。如果需要重复执行某一条命令,就可以不断键入`!!`,这样非常方便。它等同于`!-1`。 |
| 125 | + |
| 126 | +```bash |
| 127 | +$ echo hello |
| 128 | +hello |
| 129 | + |
| 130 | +$ !! |
| 131 | +echo hello |
| 132 | +hello |
| 133 | +``` |
| 134 | + |
| 135 | +上面示例中,`!!`会返回并执行上一条命令`echo hello`。 |
| 136 | + |
| 137 | +有时候,我们使用某条命令,系统报错没有权限,这时就可以使用`sudo !!`。 |
| 138 | + |
| 139 | +```bash |
| 140 | +# 报错,没有执行权限 |
| 141 | +$ yum update |
| 142 | + |
| 143 | +$ sudo !! |
| 144 | +sudo yum update |
| 145 | +``` |
| 146 | + |
| 147 | +上面示例中,`sudo !!`返回`sudo yum update`,从而就可以正确执行了。 |
| 148 | + |
| 149 | +### ! + 搜索词 |
| 150 | + |
| 151 | +`感叹号 + 搜索词`可以快速执行匹配的命令。 |
| 152 | + |
| 153 | +```bash |
| 154 | +$ echo Hello World |
| 155 | +Hello World |
| 156 | + |
| 157 | +$ echo Goodbye |
| 158 | +Goodbye |
| 159 | + |
| 160 | +$ !e |
| 161 | +echo Goodbye |
| 162 | +Goodbye |
| 163 | +``` |
| 164 | + |
| 165 | +上面例子中,`!e`表示找出操作历史之中,最近的那一条以`e`开头的命令并执行。Bash 会先输出那一条命令`echo Goodbye`,然后直接执行。 |
| 166 | + |
| 167 | +同理,`!echo`也会执行最近一条以`echo`开头的命令。 |
| 168 | + |
| 169 | +```bash |
| 170 | +$ !echo |
| 171 | +echo Goodbye |
| 172 | +Goodbye |
| 173 | + |
| 174 | +$ !echo H |
| 175 | +echo Goodbye H |
| 176 | +Goodbye H |
| 177 | + |
| 178 | +$ !echo H G |
| 179 | +echo Goodbye H G |
| 180 | +Goodbye H G |
| 181 | +``` |
| 182 | + |
| 183 | +注意,`感叹号 + 搜索词`语法只会匹配命令,不会匹配参数。所以`!echo H`不会执行`echo Hello World`,而是会执行`echo Goodbye`,并把参数`H`附加在这条命令之后。同理,`!echo H G`也是等同于`echo Goodbye`命令之后附加`H G`。 |
| 184 | + |
| 185 | +由于`感叹号 + 搜索词`会扩展成以前执行过的命令,所以含有`!`的字符串放在双引号里面,必须非常小心,如果它后面有非空格的字符,就很有可能报错。 |
| 186 | + |
| 187 | +```bash |
| 188 | +$ echo "I say:\"hello!\"" |
| 189 | +bash: !\: event not found |
| 190 | +``` |
| 191 | + |
| 192 | +上面的命令会报错,原因是感叹号后面是一个反斜杠,Bash 会尝试寻找,以前是否执行过反斜杠开头的命令,一旦找不到就会报错。解决方法就是在感叹号前面,也加上反斜杠。 |
| 193 | + |
| 194 | +```bash |
| 195 | +$ echo "I say:\"hello\!\"" |
| 196 | +I say:"hello\!" |
| 197 | +``` |
| 198 | + |
| 199 | +### !? + 搜索词 |
| 200 | + |
| 201 | +`!? + 搜索词`可以搜索命令的任意部分,包括参数部分。它跟`! + 搜索词`的主要区别是,后者是从行首开始匹配。 |
| 202 | + |
| 203 | +```bash |
| 204 | +$ cat hello.txt |
| 205 | +Hello world ..! |
| 206 | + |
| 207 | +$ !?hello.txt |
| 208 | +cat hello.txt |
| 209 | +Hello world ..! |
| 210 | +``` |
| 211 | + |
| 212 | +上面示例中,`!?hello.txt`会返回最近一条包括`hello.txt`的命令。 |
| 213 | + |
| 214 | +### !$,!* |
| 215 | + |
| 216 | +`!$`代表上一个命令的最后一个参数,它的另一种写法是`$_`。 |
| 217 | + |
| 218 | +`!*`代表上一个命令的所有参数,即除了命令以外的所有部分。 |
| 219 | + |
| 220 | +```bash |
| 221 | +$ cp a.txt b.txt |
| 222 | +$ echo !$ |
| 223 | +b.txt |
| 224 | + |
| 225 | +$ cp a.txt b.txt |
| 226 | +$ echo !* |
| 227 | +a.txt b.txt |
| 228 | +``` |
| 229 | + |
| 230 | +上面示例中,`!$`代表上一个命令的最后一个参数(`b.txt`),`!*`代表上一个命令的所有参数(`a.txt b.txt`)。 |
| 231 | + |
| 232 | +如果想匹配上一个命令的某个指定位置的参数,使用`!:n`。 |
| 233 | + |
| 234 | +```bash |
| 235 | +$ ls a.txt b.txt c.txt |
| 236 | + |
| 237 | +$ echo !:2 |
| 238 | +b.txt |
| 239 | +``` |
| 240 | + |
| 241 | +上面示例中,`!:2`返回上一条命令的第二个参数(`b.txt`)。 |
| 242 | + |
| 243 | +这种写法的`!:$`,代表上一个命令的最后一个参数。事实上,`!$`就是`!:$`的简写形式。 |
| 244 | + |
| 245 | +```bash |
| 246 | +$ ls a.txt b.txt c.txt |
| 247 | + |
| 248 | +$ echo !:$ |
| 249 | +echo c.txt |
| 250 | +c.txt |
| 251 | +``` |
| 252 | + |
| 253 | +上面示例中,`!:$`代表上一条命令的最后一个参数(`c.txt`)。 |
| 254 | + |
| 255 | +如果想匹配更久以前的命令的参数,可以使用`!<命令>:n`(指定位置的参数)和`!<命令>:$`(最后一个参数)。 |
| 256 | + |
| 257 | +```bash |
| 258 | +$ ls !mkdir:$ |
| 259 | +``` |
| 260 | + |
| 261 | +上面示例中,`!mkdir:$`会返回前面最后一条`mkdir`命令的最后一个参数。 |
| 262 | + |
| 263 | +```bash |
| 264 | +$ ls !mk:2 |
| 265 | +``` |
| 266 | + |
| 267 | +上面示例中,`!mk:2`会返回前面最后一条以`mk`开头的命令的第二个参数。 |
| 268 | + |
| 269 | +### !:p |
| 270 | + |
| 271 | +如果只是想输出上一条命令,而不是执行它,可以使用`!:p`。 |
| 272 | + |
| 273 | +```bash |
| 274 | +$ echo hello |
| 275 | + |
| 276 | +$ !:p |
| 277 | +echo hello |
| 278 | +``` |
| 279 | + |
| 280 | +上面示例中,`!:p`只会输出`echo hello`,而不会执行这条命令。 |
| 281 | + |
| 282 | +如果想输出最近一条匹配的命令,而不执行它,可以使用`!<命令>:p`。 |
| 283 | + |
| 284 | +```bash |
| 285 | +$ !su:p |
| 286 | +``` |
| 287 | + |
| 288 | +上面示例中,`!su:p`会输出前面最近一条以`su`开头的命令,而不执行它。 |
| 289 | + |
| 290 | +## `^string1^string2` |
| 291 | + |
| 292 | +`^string1^string2`用来执行最近一条包含`string1`的命令,将其替换成`string2`。 |
| 293 | + |
| 294 | +```bash |
| 295 | +$ rm /var/log/httpd/error.log |
| 296 | +$ ^error^access |
| 297 | +rm /var/log/httpd/access.log |
| 298 | +``` |
| 299 | + |
| 300 | +上面示例中,`^error^access`将最近一条含有`error`的命令里面的`error`,替换成`access`。 |
| 301 | + |
| 302 | +## histverify 参数 |
| 303 | + |
| 304 | +上面的那些快捷命令(比如`!!`命令),都是找到匹配的命令后,直接执行。如果希望增加一个确认步骤,先输出是什么命令,让用户确认后再执行,可以打开 Shell 的`histverify`选项。 |
| 305 | + |
| 306 | +```bash |
| 307 | +$ shopt -s histverify |
| 308 | +``` |
| 309 | + |
| 310 | +打开`histverify`这个选项后,使用`!`快捷键所返回的命令,就会先输出,等到用户按下回车键后再执行。 |
| 311 | + |
| 312 | +## 快捷键 |
| 313 | + |
| 314 | +下面是其他一些与操作历史相关的快捷键。 |
| 315 | + |
| 316 | +- `Ctrl + p`:显示上一个命令,与向上箭头效果相同(previous)。 |
| 317 | +- `Ctrl + n`:显示下一个命令,与向下箭头效果相同(next)。 |
| 318 | +- `Alt + <`:显示第一个命令。 |
| 319 | +- `Alt + >`:显示最后一个命令,即当前的命令。 |
| 320 | +- `Ctrl + o`:执行历史文件里面的当前条目,并自动显示下一条命令。这对重复执行某个序列的命令很有帮助。 |
| 321 | + |
| 322 | +## 参考链接 |
| 323 | + |
| 324 | +- [Bash bang commands: A must-know trick for the Linux command line](https://www.redhat.com/sysadmin/bash-bang-commands) |
| 325 | + |
0 commit comments