【转】Bash命令行历史权威指南
转自:逍遥云's Blog
本文PDF格式电子书下载:
基本的历史命令快捷键
你可能记得,Bash提供了两种编辑命令的模式:emacs与vi, 很多快捷键在不同的编辑模式下是不同的。 切换模式:
1$ set -o mode (vi/emacs)
假设你要执行如下命令:
1$ echo foo bar baz
2$ iptables -L -n -v -t nat
3$ ... lots and lots more commands
4$ echo foo foo foo
5$ perl -wle 'print q/hello world/'
6$ awk -F: '{print$1}' /etc/passwd
然后你想执行最后一条历史命令(awk -F ...).
你会想当然的按下键盘的上箭头并切安逸地陪伴你一生,但是真的需要把你的手移动哪么远吗?
如果在Emacs模式下你只用按下Ctrl-P就能够从历史记录中取到前一条命令(Ctrl-n取下一条命令)
在vi模式下你只要按下 Ctrl-[
(或ESC)(用来切换到命令模式)与 'h'('j'取下一条命令)即可
这儿有一个等同有效的方式就是Bash的历史扩展机制--事件引用符,输入"!!"就可以达到以上等同的效果(详细后面说明)
现在,假设你不想通过再次重复输入就想执行以上第二条命令iptables -L -n -v -t nat
天真的用户会照常按下键盘上箭头直到找那所需的那条命令,但这并不是黑客们的玩转方式,黑客们喜欢快速有效的方式。忘掉键盘的上下右左键以及翻页那一块键吧,依我看,它们跟本用不着,而且离键盘主要工作区域太远了。(看来作者应该玩的是vi)
在emacs模式下输入iptables
前几个字符,然后使用Ctrl-r就行了,比如:ipt
,
这将会正确地得到最后的以iptables
起始的命令,再次输入Ctrl-r会得到更老输入的匹配,假如你错过了正确的结果,你要通过Ctrl-s就可以反向的搜索了(不要忘记默认的Ctrl-s操作会停止终端的输入,看起来像是假死了,记得按下Ctrl-g让终端恢复正常,具体设置,参见sttycommand
)。
在vi模式下使用Ctrl-r与Ctrl-s会有些不同。
通过使用Ctrl-[
或ESC
结合/
来切换到命令模式,输入iptables
前几个字符(插入模式),如ipt
,然后回车.
Bash将会匹配到最近的一条以ipt
开始的历史记录,输入n
或者通过/
再次操作会得到相关的上一条命令,使用N
or ?
得到下一条命令。
通过事件引用符可以得到最最近以string
开头的历史命令
而使用!iptables
就能扩展到最近的以iptables
开头的历史命令
另一种方式是使用Bash自身的history
命令与grep结合,然后再使用事件引用符!N
, N指的是历史记录中的第几条命令。
例如:
1$ history | grep 'ipt'
22 iptables -L -n -v -t nat
3$ !2 # will execute the iptables command
在vi编辑模式下,使用N
(命令行号)与G
,就可以达到相同的效果,此例使用2G
即可
列出与擦除历史命令
Bash内建了历史命令(history
)的查看与擦除
及以下例为例:
1$ echo foo bar baz
2$ iptables -L -n -v -t nat
3$ ... lots and lots more commands
4$ echo foo foo foo
5$ perl -wle 'print q/hello world/'
6$ awk -F: '{print$1}' /etc/passwd
输入history
将会得到所有的带行号的历史命令
11 echo foo bar baz
22 iptables -L -n -v -t nat
3... lots and lots more commands
4568 echo foo foo foo
5569 perl -wle 'print q/hello world/'
6570 awk -F: '{print$1}' /etc/passwd
输入history N
(N为整数),会得到最近的N条历史命令,如:history 3
,结果如下:
1568 echo foo foo foo
2569 perl -wle 'print q/hello world/'
3570 awk -F: '{print$1}' /etc/passwd
history -c
将清除所以的历史记录,history -d N
将会删除N条历史记录
默认在用户的主目录下会有一个名为.bash_history
的历史记录文件
历史命令扩展
历史命令扩展的完善就是依赖所谓的事件引用符与词组引用符,事件引用符适用于先前取得的命令(事件),词组引用符适用于由事件而来的命令行。我们可以选择的就是:多样的修饰符可应用于已提取的参数。
事件引用符
事件引用符都是的以感叹号开头的特殊命令(也有以 ^
开头的),它们可跟一个词组引用符和一个或多个修饰符。引用符与修饰符都是由:
分隔开的
让我们看看下面的一组事件引用符的例子是如何运作的。
事件引用符!!
用来取得前一条命令,例如:
1$ echo foo bar baz
2foo bar baz
3$ !!
4foo bar baz
这里的!!
就执行了echo foo bar baz
这条命令
事件引用符!N
用于执行命令历史的第N条记录
假如你的历史记录输出如下:
11 echo foo foo foo
22 iptables -L -n -v -t nat
3... lots and lots more commands
4568 echo bar bar bar
5569 perl -wle 'print q/hello world/'
6570 awk -F: '{print$1}' /etc/passwd!
!569
就会执行perl …
这条命令,而!1
就会执行echo foo foo foo
!-N
就会执行当前的命令的计数前N条命令(中间的-
可以理解为减号),如下:
1$ echo foo bar baz
2foo bar baz
3$ echo a b c d e
4a b c d e
5$ !-2
6foo bar baz
!-2
执行上上一条命令,或者说是当前的命令的前2条命令
!string
适用以string
开头的历史命令。例如:
1$ awk --help
2$ perl --help
!p
或者!perl
或者!per
都可以正确是执行到perl -help
,同理,!a
亦可以执行那条以awk开头的命令
面!?string?
就能匹配到包含string
的命令,并不严格要求开头。
最有意思的事件引用符莫过于^string1^string^
了,它将替换最后一条命令中的string1
字符窜为string2
,例如:
1$ ehco foo bar baz
2bash: ehco: command not found
3$ ^ehco^echo^
4foo bar baz
上面的^ehco^echo^
作用就是取得上一条历史记录并替换ehco为echo,然后再执行
词组引用符与修饰符
事件引用符后面词组引用符(一个或多个)是用冒句分隔开的,它们适用于当前参照命令的部分或者所有的参数
例如:
1$ echo a b c d e
2a b c d e
3$ echo !!:2
4b
这里有一个最简单的词组引用符,:2
对应第2个参数(第3个单词),通常情况下:N
对应命令的第N-1个单词
词组引用符也接受一个范围值,例如:
1$ echo a b c d e
2a b c d e
3$ echo !!:3-4
4c d
符号用法也是多样的,例如,:$
对应最后一个参数,:^
对应第一个参数,:*
对应所有的参数(等同:1-$
),等等,详情看完整的参照表
修饰符可以改变单词引用符的行为,例如:
1$ tar -xvzf software-1.0.tgz
2software-1.0/file
3...
4$ cd !!:$:r
5software-1.0$
r
在这里就匹配了上一条命令的最后一个参数,r
在此用作去掉后缀.tgz
h
用于匹配并删除文件路径中的文件名部分,返回文件路径的开头部分:
1$ echo /usr/local/apache
2/usr/local/apache
3$ echo !!:$:h
4/usr/local
e
则只保留扩展名
1$ ls -la /usr/src/software-4.2.messy-Extension
2...
3$ echo /usr/src/*!!:$:e
4/usr/src/*.messy-Extension # ls could have been used instead of echo
另一个有趣的修饰符是替换符:s/old/new/
,即替换单词old
为new
,g
用于全局替换,例如:
1$ ls /urs/local/software-4.2 /urs/local/software-4.3
2/usr/bin/ls: /urs/local/software-4.2: No such file or directory
3/usr/bin/ls: /urs/local/software-4.3: No such file or directory
4$ !!:gs/urs/usr/
5...
以上这个例子就是替换所有的 urs
为 usr
来达到我们想要的结果。
这里还有一些其它的修饰符,如 p
只是用来回显扩展过的命令,并不执行。详情看完整的参照表。
配置命令历史
Bash允许用户自己配置命令历史文件,文件保存着命令以及记录号和一些选项
变量 HISTFILE, HISTFILESIZE, HISTIGNORE 和 HISTSIZEenvironment 对命令历史起着决定的作用。
HISTFILE, 正如词面意思,代表命令历史文件的路径
1$ export HISTFILE=/home/pkrumins/todays_history
上面命令当保存命令历史文件路径为 /home/pkrumins/todays_history
值设置为 /dev/null
或空将不保存历史记录
HISTFILESIZE 控制着历史文件最大记录数,例如:
1$ export HISTFILESIZE=1000
这将保存1000条最近的历史命令
HISTSIZE 控制着当前会话(终端)的历史记录条数
1$ export HISTSIZE=42
上面将保存当前会话最近42条历史命令
如果HISTSIZE变量的值小于HISTFILESIZE变量的值,只有限定数量的命令会被记录下来
HISTIGNORE用来控制我们不想记录的命令,此变量是用冒号来分隔不同的模式,&
对匹配历史命令起来特殊的作用。
[ ]\*
这个技巧可以用来忽略以空格开头的命令。
例如:
1$ export HISTIGNORE="&:[ ]*:exit"
上面的命令就控制bash忽略复制命令,以及以空格开头,以及内容为 exit
字符的命令
1There are several other options of interest controlled by the built-in 'shopt' command.
这儿有几个有关shopt
命令的选项。
-s
参数让shopt
命令的配置有效,而-u
参数使其无效。
histappend
控制着历史列表如何写到命令历史记录中,设置这个选项将追加当前会话的历史命令到命令历史记录中,不设置(默认不设置,我的ubuntu 10.04默认却是打开的)每次将会重写命令历史记录文件。
打开:
1$ shopt -s histappend
关闭:
1$ shopt -u histappend
选项 histreedit
允许用户重编辑一条有误的历史替换命令
试想你已经输入如下:
1$ echo foo bar baz
本意想通过^baz^test^
替换baz
为test
, 但是却输成了^boo^test^
,这将因为不匹配而导致替换失败
如果将此选项打开,bash将会保持你当前的^boo^test^
错误的输入
最后,选项histverify
允许用户去验证一条替换历史扩展命令
就拿前面的例子来说吧,假设你想通过!!
再次echo
命令,如果这个选项打开,bash将不会立即执行这条echo
命令,而是将命令显示好等待你去验证是否是你想要的结果。
调整命令提示
我的命令提示是这样的:
1Wed Jan 30@07:07:03
2pkrumins@catonmat:1002:2:~$
第一行显示日期与时间可以及时的追溯命令
第二行显示用户名,主机名,全局行号以及当前命令当号
全局行号可以让我更快捷地使用事件引用符
我的PS1参数,变量值如下:
1PS1='\d@\t\n\u@\h:\!:\#:\w$ '
- 文章ID:1254
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/via-bash-comman-history/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。