这是 Bash One-Liners Explained 系列的第四篇文章。在这一篇里,我会给大家介绍 Bash 命令行历史功相关的内容。我会选择用最合适的 Bash 方法,各种常见的语法和技巧,向各位阐明如何用 Bash 内置的命令和 Bash 编程语言来完成各式各样的任务。
1. 清除命令行历史
$ rm ~/.bash_history
Bash 将历史执行的命令都保存在文件.bash_history
中,该文件位于你的家目录下。为了清除命令行历史,只要把这个文件删除即可。
注意,当你执行完退出后,最后一个rm ~/.bash_history
命令依然会被记录下来。如果你想隐藏清除的操作命令,请看下一条。
2. 当前会话下停止记录命令行历史
$ unset HISTFILE
环境变量HISTFILE指向命令行执行历史保存的目标文件路径,如果你重置了该变量,Bash 就不会保存历史。
另外一种方法是将它指向/dev/null
:
$ HISTFILE=/dev/null
3. 不要记录当前执行的命令
很简单,只要在命令之前加空格就行:
$ command
注意,以上正常工作的前提是,HISTIGNORE
变量被正确的设置,它的值是冒号分隔的匹配表达式列表,如果一个命令匹配其中的任意一个表达式则不会被保存到记录中。
例如,忽略空格开头的命令:
HISTIGNORE="[ \t]*"
我(原文作者)的配置如下所示:
HISTIGNORE="&:[ \t]*"
这里的&
符号是有特殊含义的,它表示上一次执行的命令。所以,这里除了忽略空格开头的命令之外,重复执行的命令也只会被记录一次。
4. 更改保存命令行历史的目标文件
$ HISTFILE=~/docs/shell_history.txt
之后执行的命令会被记录到文件~/docs/shell_history.txt
中。
5. 命令行历史记录中增加时间戳
$ HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S"
如果你将环境变量HISTTIMEFORMAT
设置成一个合法的日期格式(具体请参考man 3 strftime
),Bash 会在历史记录中同时保存命令执行的时间,而且在你执行history
命令时,它也会将时间显示出来。
6. 显示历史
$ history
history
命令可以按行显示执行的历史命令,如果设置了HISTTIMEFORMAT
,在结果中还会显示命令执行的时间。
7. 显示最近执行的50个命令
$ history 50
如果你在执行history
命令时,指定一个数字参数,例如 50,那么它只会显示最近50个命令。
8. 显示执行最多的10个命令
$ history | sed 's/^ \+//;s/ / /' | cut -d' ' -f2- | awk '{ count[$0]++ } END { for (i in count) print count[i], i }' | sort -rn | head -10
这一行命令中结合了sed
, cut
, awk
, sort
和head
等多个命令。让我们来回顾下整个过程,理解发生了什么。假设history
命令的输出是这样的:
$ history 1 rm .bash_history 2 dmesg 3 su - 4 man cryptsetup 5 dmesg
首先,我们通过sed
命令删除开头的空格,同时将行号后的连续两个空格替换成一个:
$ history | sed 's/^ \+//;s/ / /' 1 rm .bash_history 2 dmesg 3 su - 4 man cryptsetup 5 dmesg
接下来,我们使用cut
命令将第一列删除:
$ history | sed 's/^ \+//;s/ / /' | cut -d' ' -f2- rm .bash_history dmesg su - man cryptsetup dmesg
然后,我们再用awk
命令统计命令在历史记录中出现的次数:
$ history | sed 's/^ \+//;s/ / /' | cut -d' ' -f2- | awk '{ count[$0]++ } END { for (i in count) print count[i], i }' 1 rm .bash_history 2 dmesg 1 su - 1 man cryptsetup
紧接着,使用sort
命令逆序排列结果:
$ history | sed 's/^ \+//;s/ / /' | cut -d' ' -f2- | awk '{ count[$0]++ } END { for (i in count) print count[i], i }' | sort -rn 2 dmesg 1 rm .bash_history 1 su - 1 man cryptsetup
最后,我们截取最开始的10行,即10个最频繁实用的命令。
$ history | sed 's/^ \+//;s/ / /' | cut -d' ' -f2- | awk '{ count[$0]++ } END { for (i in count) print count[i], i }' | sort -rn | head -10
以下是我的使用最多的10个命令:
2172 ls 1610 gs 252 cd .. 215 gp 213 ls -las 197 cd projects 155 gpu 151 cd 119 gl 119 cd tests/
9. 快速执行上一个命令
$ !!
输入两个感叹号,第一个感叹号表示开始历史命令替换,而第二个感叹号表示上一次执行的命令。例如:
$ echo foo foo $ !! foo
这里echo foo
被重复执行了一次。
这个用法在你忘记通过sudo
执行命令时尤其有用,例如:
$ rm /var/log/something rm: cannot remove `/var/log/something': Permission denied $ $ sudo !! # executes `sudo rm /var/log/something
10. 快速执行最近一个以特定字符串开头的命令
$ !foo
上一个命令中,第一个感叹号表示开始历史命令替换,后面的内容表示最近一次执行的以foo
开头的命令。例如:
$ echo foo foo $ ls / /bin /boot /home /dev /proc /root /tmp $ awk -F: '{print $2}' /etc/passwd ... $ !ls /bin /boot /home /dev /proc /root /tmp
11. 使用文本编辑器打开上一次执行的命令
$ fc
当fc
命令执行后,会用文本编辑器打开上一个命令。当你想要编辑一个很长并且复杂的命令式,这个功能会帮你省下不少功夫。
例如,你输入了下面一行错误的命令:
$ for wav in wav/*; do mp3=$(sed 's/\.wav/\.mp3/' <<< "$wav"); ffmpeg -i "$wav" "$m3p"; done
当你输完命令后,因为内容过长,你找不出错误的地方。这中情况下,你可以使用fc
命令加载该命令到文本编辑器中,然后错误的地方(最后的 mp3 单词拼错)就一目了然了。
不好意思,看了译文五之后知道了方法:
注:设置默认的编辑器方法,例如 vim:export EDITOR='vim'
请问可以转载到自己的blog中么?(我之前修改了博主之前的新浪博客博文下载的Python代码放在了自己的blog中,给了GitHub的链接,不知可以否?)
@_zero:没有问题,欢迎多交流 🙂
最后一个方法大赞啊,不过如何修改fc的默认文本编辑器呢?一般习惯用vim而不是Nano
@_zero:
export EDITOR=vim
“接下来,我们使用cut命令将第一行删除”应为“接下来,我们使用cut命令将第一列删除”
“而第二个命令表示上一次执行的命令”应为“而第二个感叹号表示上一次执行的命令”
@pysense:多谢,已经修改:)
@kodango:虽然很基础,但很多用法对我这新手还是第一次接触,所以看的比较认真,谢谢你提供的“连载”系列,我才抽空看了两篇文章,慢慢消化:)