Linux命令行学习笔记:命令的重新运行
查看历史命令
使用shell内置的history命令可以查看我们在交互式shell中执行的命令。history输出的结果可能是成百上千行,在history后加上整数可以将输出限制在最新几行:
1 | history 3 |
1 | 34 ls never_delete_wrong_file_1 |
history向stdout中输出,因此我们可以用管道处理。
在当前shell中清楚(删除)历史,可以用-c选项:
1 | history -c |
从历史中重新召唤命令
三种从shell历史记录中回忆命令的节省时间的方法:
光标移动
极其容易学习,但在实践中往往速度较慢
历史展开
学习起来较难(坦白说,它很晦涩难懂),但可以非常快
增量搜索
既简单又快速
光标移动
按上/下箭头选择历史命令。
历史展开
历史展开是一个通过特殊表达式获取历史命令的shell特性。表达式以感叹号(读作"bang")开头。例如,一行中的两个感叹号("bang bang")执行紧接着的前一个命令。
1 | ignoreboth |
1 | !! |
1 | echo $HISTCONTROL |
要获得以特定字符开头的最近的命令,在该字符串前放置一个感叹号:
1 | !grep |
1 | grep python animals.txt |
要获得以特定字符在某处的最近的命令,在该字符串前放置一个感叹号,并把该字符串用问号包围:
1 | !?grep? |
1 | history | grep -w cd |
也可以用历史ID来执行某一特定历史命令(在ID前加感叹号):
1 | !28 |
1 | python3 |
感叹号后的负数用于以相对位置执行历史命令,例如!-3意味着“你执行的前第3条命令”。
历史展开高效而快速,但有些神秘。然而,它也可能很危险,如果我们执行了错误的代码。为了避免致命的错误,我们应该使用:p选项来打印而不执行命令:
1 | !-5:p |
1 | python3 |
shell将未执行的命令添加到历史中,因此我们只需
1 | !! |
就可以快速执行了。
关于历史命令的常见问题
命令历史中储存了多少行命令?
历史中储存了HISTSIZE行命令。
1 | echo $HISTSIZE |
1 | 1000 |
1 | HISTSIZE=10000 |
计算机记忆体是十分廉价的(10000行历史命令大约仅占用200K记忆体)。大胆的话,我们可以将HISTSIZE的值设置为-1以保存所有命令。
加入命令历史的是什么样的文本?
Shell会原封不动地追加你输入的内容,不进行求值。如果你运行ls $HOME,历史记录中将包含ls $HOME,而不是ls /home/smith。
需要注意的是输入!!或!-3这样的文本时,命令会先执行再被添加到历史中。
重复的命令会被追加到历史中吗?
答案取决于变
HISTCONTROL的值。默认情况下,如果这个变量未设置,那么每条命令都会被追加。如果其值为ignoredups,那么如果重复的命令是连续的,则不会被追加(其他值可参见man bash):
1 | $HISTCONTROL=ignoredups |
每个shell是否都有一个独立的历史记录,还是所有shell共享一个单一的历史记录?
每个交互式shell都有一个独立的历史记录。
永远不要再次删除错误的文件
使用alias
我们有时候使用模式匹配删除文件时会错误地输入而删除了的文件,例如我们要使用模式匹配*.txt,而错误地多打了一个空格:
1 | rm * .txt |
避免这种问题最常见的方法就是将rm -i的别名设置为rm,这样的话再删除文件前shell会询问:
1 | alias rm='rm -i' |
1 | rm: remove regular file '1.txt'? y |
如此一来,多打的空格就不会致命了:
1 | rm * .txt |
1 | rm: remove regular file '123'? ^C |
如果发现错误,立即终止命令(Control + C)。
先确认再删除
先使用ls列举所选文件:
1 | ls *.txt |
1 | 1.txt 2.txt 3.txt |
确认无误后再删除:
1 | rm !$ |
1 | rm *.txt |
历史展开式!$意思是前一个命令的最后一个词。
命令历史的增量搜索
增量搜索(incremental search)跟搜索引擎提供的交互式建议很像。在大多数情况下,增量搜索是从历史中重新召回命令的最快捷、最简单的方法。
- 在命令提示符处按下CTRL+R(R代表reverse incremental search)
- 输入原来命令的任意一部分(开头、中间、结尾……)
- 每当输入一个字符时,shell都会展示最近的与刚刚输入的字符匹配的命令
- 当看到需要的命令时,按下ENTER
按下CTRL+R后:
1 | (reverse-i-search)`': |
但如果我们又一个命令完全包含了另一个命令呢?
可以再次按下CTRL+R,shell会自动跳转到历史中上一个匹配的命令。
命令行编辑
我们有如下几种命令行编辑的方法:
- 光标移动
- 插入符号表示法
- Emacs或Vim风格的按键操作
在命令中光标移动
有如下操作:
| 操作 | 功能(效果) |
|---|---|
| 左箭头 | 向左移动一个字符 |
| 右箭头 | 向右移动一个字符 |
| CTRL+左箭头 | 向左移动一个单词 |
| CTRL+右箭头 | 向右移动一个单词 |
| Home | 前往命令开头 |
| End | 前往命令结尾 |
插入符号的命令展开
假设下列命令中我们错误的将jpg打为jg:
1 | md5sum *.jg | cut -c1-32 | sort | uniq -c | sort -nr |
我们不需要将命令重新打一遍,只需(即caret syntax):
1 | ^jg^jpg |
就可显示命令已替换成功
1 | md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr |
[!NOTE]
使用这种方式只能替换命令中出现的第一个源字符串(
jg)。
Emacs或Vim风格的按键操作
使用
1 | set -o vi |
或
1 | set -o emacs |
现在,我们就可以使用我们熟悉的编辑风格编辑命令行了!
剩下的就是练习、练习、再练习!
