介绍
在服务器环境中工作时,您将花费大量时间在命令行上,最有可能的是,您将使用 bash shell,这是大多数发行版的默认。
在终端会话中,你可能会经常重复某些命令,并更频繁地在这些命令中输入变异。
幸运的是,bash shell 具有一些相当发达的历史功能,学习如何有效地使用和操纵你的bash 历史将允许你花费更少的时间打字和更多的时间来完成实际工作,许多开发人员都熟悉 DRY 哲学 Don't Repeat Yourself. 有效地使用bash 历史可以让你更接近这个原则,并加快工作流程。
前提条件
要跟随这个指南,你需要访问运行基于Linux的操作系统的计算机. 它可以是一个虚拟的私人服务器,你已经连接到SSH或你的本地机器. 请注意,本教程是通过使用运行Ubuntu 20.04的Linux服务器验证的,但所示的例子应该在运行任何Linux发行版的计算机上工作。
如果您打算使用远程服务器来遵循本指南,我们建议您先完成我们的 初始服务器设置指南。
历史上的缺陷
在您开始实际使用命令历史之前,您可以调整一些bash设置以使其更有用,这些步骤并不必要,但它们可以使您更容易找到和执行您之前运行过的命令。
Bash 允许你调整它存储在历史上的命令数量,实际上它有两个独立的选项:‘HISTFILESIZE’参数配置了历史文件中存储的命令数量,而‘HISTSIZE’控制了当前会话中存储在记忆中的数量。
这意味着您可以为当前会话内存中的历史大小设置合理的限制,并将更大的历史记录存储到磁盘上,您可以稍后检查。默认情况下,bash 会为这些选项设置非常保守的值,但您可以扩展这些值以利用更大的历史记录。
使用您喜爱的文本编辑器打开您的 ~/.bashrc
文件以更改这些设置. 在这里,我们将使用 nano
:
1nano ~/.bashrc
搜索HISTSIZE
和HISTFILESIZE
参数。如果设置了这些参数,请自由修改这些值。如果这些参数不在您的文件中,请现在添加它们。 对于本指南的目的,将 10000 行保存到磁盘上,并将最后 5000 行加载到内存中会很好。 对于大多数系统来说,这是一个保守的估计,但如果您注意到性能影响,您可以调整这些数字:
1[label ~/.bashrc]
2. . .
3
4# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
5HISTSIZE=5000
6HISTFILESIZE=10000
7
8. . .
默认情况下,bash 会在每个会话结束时写下其历史记录,将现有文件重写为更新的版本,这意味着如果您在多个bash 会话中登录,只有最后一个退出的文件才会保存其历史记录。
您可以通过设置histappend
设置来处理此问题,而不是重写历史记录。
1[label ~/.bashrc]
2. . .
3shopt -s histappend
4. . .
如果您想让 bash 立即将命令添加到您的历史记录中,而不是等待每个会话的结束(以便一个终端中的命令在另一个终端中立即可用),您也可以将历史 -a
命令设置或附加到 PROMPT_COMMAND
参数中,该参数包含在每个新命令提示之前执行的命令。
要正确配置此功能,您需要自定义 bash 的PROMPT_COMMAND
,以更改命令在历史文件和当前壳会话的内存中记录的方式:
- 首先,您需要立即附加到历史文件中的
历史 -a
- 接下来,您必须用
历史 -c
清除您的壳会话中的当前历史。
将所有这些命令放在PROMPT_COMMAND
壳变量中的顺序中会导致以下情况,您可以将其粘贴到您的.bashrc
文件中:
1[label ~/.bashrc]
2. . .
3export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
4. . .
如果你用nano
编辑了你的.bashrc
文件,请按CTRL + X
,Y
,然后按ENTER
。
要实施更改,要么退出并再次登录,要么通过运行源
文件:
1source ~/.bashrc
有了这个,你已经调整了你的壳如何处理你的命令历史. 你现在可以得到一些练习,以历史
命令找到你的以前的命令。
查看您之前的 Bash 历史
查看您的 bash 历史的方法是使用历史
命令,这将打印我们的最近的命令,每行一个命令。这应该输出,最多,您选择的HISTSIZE
变量的行数。
1history
1[secondary_label Output]
2 . . .
3
4 43 man bash
5 44 man fc
6 45 man bash
7 46 fc -l -10
8 47 history
9 48 ls -a
10 49 vim .bash_history
11 50 history
12 51 man history
13 52 history 10
14 53 history
每个命令历史
返回与一个数字相关联,以便轻松参考。
例如,如果您只想返回已执行的最后5个命令,您可以键入:
1history 5
1[secondary_label Output]
2 50 history
3 51 man history
4 52 history 10
5 53 history
6 54 history 5
要找到包含特定字符串的所有历史
命令,您可以将结果导向一个抓住
命令,该命令搜索每个字符串的特定字符串。
1history | grep cd
1[secondary_label Output]
2 33 cd Pictures/
3 37 cd ..
4 39 cd Desktop/
5 61 cd /usr/bin/
6 68 cd
7 83 cd /etc/
8 86 cd resolvconf/
9 90 cd resolv.conf.d/
有许多情况下,能够检索您之前运行过的命令列表可以是有帮助的. 如果您想再次运行其中一个命令,您的本能可能是从输出中复制一个命令并粘贴到提示中。
从 Bash 历史记录中执行命令
打印您的命令历史记录可能有用,但它并不真正有助于您访问这些命令,而不是作为参考。
您可以回忆并立即执行任何由历史
操作返回的命令,其号码以呼叫点(!
)为前方。假设您的历史
结果与上一节中的结果一致,您可以通过键入快速检查历史
命令的男性页面:
1!51
这会立即回忆并执行与历史号51相关的命令。
您还可以使用!n
语法执行与当前位置相关的命令,其中n
被您想要回忆的以前命令的数量取代。
例如,假设您运行了以下两个命令:
1ls /usr/share/common-licenses
2echo hello
如果您想回忆并执行您在最新的命令(ls
命令)之前运行的命令,您可以键入!-2
:
1!-2
要重新執行你最後執行的命令,你可以執行 !-1
. 然而, bash 提供了由兩個呼叫點組成的捷徑,將取代最新的命令並執行它:
1!!
许多人在输入命令时使用这种方式,但忘记了他们需要sudo
特权来执行命令。
1touch /etc/hello
1[secondary_label Output]
2touch: cannot touch `/etc/hello': Permission denied
1sudo !!
1[secondary_label Output]
2sudo touch /etc/hello
3[sudo] password for sammy:
这证明了这个语法的另一个属性:这些短语是纯粹的替代品,并可根据意愿在其他命令中纳入。
穿越Bash历史
有几种方式,你可以滚动你的bash历史,把每一个连续的命令在命令行编辑。
最常用的方法是按下命令提示处的向上箭头键,每一个向上箭头键的额外按下将使您进一步回到命令行历史。
如果你需要往相反的方向去,向下箭头键将以相反的方向穿过历史,最终将你带回当前的空调。
如果将你的手移动到箭头键看起来像是一个大麻烦,你可以使用CTRL + P
组合在你的命令历史记录中向后移动,并使用CTRL + N
组合再次通过你的历史记录前进。
如果你想跳回当前的命令提示,你可以通过按META + >
来做到这一点。在大多数情况下,meta
键是ALT
键,所以META + >
将意味着按ALT + SHIFT +
。
您也可以通过执行相反的动作进入命令历史的第一行,然后键入META + <
。
要总结一下,这里有一些键和密钥组合,您可以用来滚动历史并跳到任何一个端:
UP 箭头键
:在历史中向后滚动CTRL + P
:在历史中向后滚动DOWN 箭头键
:在历史中向前滚动CTRL + N
:在历史中向前滚动ALT + SHIFT +.
:跳到历史结束(最新的)ALT + SHIFT +,
:跳到历史开始(最远)
通过Bash历史搜索
虽然通过抓住
引导历史
命令可能是一个有用的方法来缩小结果,但在许多情况下并不理想。
Bash 包含搜索功能,用于其历史,使用此方法的典型方法是使用CTRL + R
密钥组合进行历史的后期搜索(最新的结果首先返回)。
例如,您可以键入CTRL + R
,然后开始键入以前的命令的一部分. 您只需要键出命令的一部分. 如果命令匹配不需要的命令,您可以再次按CTRL + R
来获得下一个结果。
如果您意外传递了您想要的命令,您可以通过键入CTRL + S
向相反方向移动,如果您已使用上一节中的键移动到历史的另一个点,并希望继续搜索,则这也很有用。
请记住,在许多终端中,CTRL + S
组合被绘制为暂停终端会话,这将拦截任何试图通过CTRL + S
到bash
,并将冻结
您的终端。
这种暂停和恢复功能在大多数现代终端中不需要,您可以通过运行以下命令轻松关闭它:
1stty -ixon
您可以将这个stty -ixon
命令添加到您的~/.bashrc
文件的末尾,以使此更改成为永久性的。
如果您现在再次尝试使用CTRL + S
搜索,它应该按照预期工作,以便您向前搜索。
搜索你输入了命令的一部分后
一个常见的场景是键入一个命令的一部分,然后才意识到你之前已经执行了它,并可以搜索历史。
使用已在命令行上搜索的正确方法是将指针移动到CTRL + A
的行开始,用CTRL + R
调用逆历史,将当前的行粘贴到CTRL + Y
的搜索中,然后再使用CTRL + R
进行逆搜索。
举个例子,假设你想在Ubuntu系统上更新你的包缓存,你最近已经输入了这个,但直到在提示中再次输入sudo
之后,你才考虑到这一点:
1sudo
在这一点上,你意识到这是你在过去一天或更长一段时间内完成的操作。你可以按CTRL + A
来将你的指针移动到行开始时。然后,按CTRL + R
来调用你的反向增量历史搜索。
接下来,按CTRL + Y
,将您刚刚从命令行复制的命令段粘贴到搜索中。
使用这样的快捷步骤起初可能看起来很无聊,但当你习惯时,它可以非常有用,当你发现自己已经输入了一半复杂的命令,并知道你将需要历史记录来完成其余部分时,这是非常有用的。
不要把它们看作是单独的密钥组合,这可能有助于你把它们看作是一个单一的复合操作,你可以轻松按下CTRL
键,然后按下A
,R
,Y
,然后按下R
。
熟悉更先进的历史扩展
本指南已经触及了bash提供的一些最基本的历史扩展技术。
!!
: 扩展到最后一个命令!n
: 扩展命令以历史数字n
。!-n
: 扩展到历史上当前命令之前的命令数n
的命令。
活动设计师
上面的三个例子是 _event designators 的例子,这些例子通常是使用某些标准回忆以前的历史命令的方法,它们是您可用的操作的选择部分。
例如,您可以通过键入类似的东西来执行您运行的最后一个ssh
命令:
1!ssh
这会搜索以ssh
开头的命令历史记录中的行。如果您想要搜索不是命令开始的字符串,则可以用?
字符围绕它。
1!?search?
您可以尝试的另一个事件标记器包括将您最后的命令中的一个字符串替换为另一个字符串。 要做到这一点,请输入一个字符串符号(^
),然后输入您想要替换的字符串,然后立即跟随另一个字符串、替换字符串和最后一个字符串。
1^original^replacement^
这会回忆起以前的命令(就像!!
一样),在命令字符串中搜索原始
的实例,并用替换
代替它,然后使用替换字符串执行命令。
例如,假设您在试图读取 `/etc/hosts 文件的内容时错误地运行此命令:
1cat /etc/hosst
1[secondary_label Output]
2cat: /etc/hosst: No such file or directory
相反,重新编写整个命令,您可以执行以下操作:
1^hosst^hosts^
这将修复前一命令中的错误,并成功执行。
名称 名称
事件标记后,您可以添加一个列(:
),然后是 word designator,以选择匹配命令的一部分。
它通过将命令划分为单词
来做到这一点,它们被定义为由whitespace分开的任何部分,这使您有机会与命令参数进行交互。
单词编号从最初的命令开始为0
,第一个参数为1
,并从那里继续。
例如,您可以列出一个目录的内容,然后决定要导航到相同的目录。
1ls /usr/share/common-licenses
2cd !!:1
在此类情况下,您可以通过删除第二个 !
和结肠来缩短此命令:
1cd !1
这将以相同的方式运作。
您可以引用第一个指标(^
)和最后一个指标($
)的参数,如果这对您的目的有意义,这些参数在使用范围而不是特定数字时更有用。
1!!:1*
2!!:1-$
3!!:*
单个*
扩展到被召回的命令的所有部分,而不是最初的命令。
修改者
您可以做另一件事来增强您正在回忆的历史行的行为是修改回忆的行为,以操纵文本本身. 要做到这一点,您可以在扩展的末尾添加 modifiers 后列(:
)字符。
例如,您可以通过使用h
编辑器(它代表头
)切断导致文件的路径,该路径将被删除,直到最后的切割(/
)字符。
一个常见的用例是,如果您正在修改文件,并意识到您想更改文件的目录,以便对相关文件执行操作。
例如,假设您运行此命令以将开源软件许可证的内容打印到输出中:
1cat /usr/share/common-licenses/Apache-2.0
在确认该许可证符合您的需求后,您可能想要更改其存储的目录,您可以通过在论点链上调用cd
命令并在末尾切除文件名来完成此操作:
1cd !!:$:h
如果您运行pwd
,它打印了当前的工作目录,您会发现您已导航到前一个命令中包含的目录:
1pwd
1[secondary_label Output]
2/usr/share/common-licenses
一旦你在那里,你可能想再次打开该许可证文件,以双重检查,这次在像少
这样的页面。
要做到这一点,你可以通过切断路径来执行前一个操作的逆转,并仅使用t
修改器的文件名,这意味着尾巴
。
1less !cat:$:t
您可以很容易地保留完整的绝对路径名称,并且这个命令在这种情况下会正常工作。然而,可能有其他时间,这种情况不正确。例如,您可以使用相对路径在当前工作目录下方的几个子目录中查看一个文件,然后使用h
编辑器更改到子目录。
另一个非常有用的编辑器是r
编辑器,它删除后续扩展. 如果您正在使用tar
来提取文件,并希望随后更改到结果的目录,这可能是有用的。
1tar xzvf long-project-name.tgz
2cd !!:$:r
如果你的 tarball 使用了tar.gz
扩展,而不是tgz
,你可以简单地通过修改器两次:
1tar xzvf long-project-name.tgz
2cd !!:$:r:r
类似的修改器e
除后续扩展外,除去一切。
如果您不想执行您正在召回的命令,并且只想找到它,您可以使用p
修改器来执行 bash 响应命令,而不是执行它。
如果您不确定是否选择正确的作品,这不仅会打印它,还会将其放入您的历史记录中,以便进一步编辑,如果您想修改它。
例如,想象一下你在主目录上运行了查找
命令,然后意识到你想从根目录(/
)中运行它。
1find ~ -name "file1"
2!119:0:p / !119:2*:p
1[secondary_label Output]
2find / -name "file1"
如果返回的命令是正确的,您可以使用CTRL + P
键组合执行它。
您也可以使用 s/original/new/
语法在命令中进行替换。
例如,您可以通过键入:
1!119:s/~/\//
这将取代搜索模式的第一个实例(~
)。
例如,如果你想创建名为file1
,file2
和file3
的文件,然后想要创建名为dir1
,dir2
,dir3
的目录,你可以这样做:
1touch file1 file2 file3
2mkdir !!:*:gs/file/dir/
当然,在这种情况下,只运行mkdir dir1 dir2 dir3
可能更直观,但是,随着您使用修改器和其他bash历史扩展工具变得舒适,您可以在命令行上大大扩展您的能力和生产力。
结论
通过阅读本指南,您现在应该有一个很好的想法,如何利用可用的历史操作. 其中一些可能比其他人更有用,但要知道bash具有这些功能,如果您发现自己处于一个有帮助地挖掘它们的位置。
如果没有别的,单靠历史
命令、反搜索和基本的历史扩展可以帮助您加速工作流程。