如何在LinuxVPS上使用BashHistory命令和扩展
介绍
在服务器环境中工作时,您将花费大量时间在命令行上。 最有可能的是,您将使用 bash shell,这是大多数发行版的默认设置。
在终端会话期间,您可能会经常重复某些命令,并且更频繁地键入这些命令的变体。 虽然在开始时重复输入每个命令可能是一种很好的做法,但在某些时候,它会越界成为破坏性和烦恼。
幸运的是,bash shell 有一些相当完善的历史功能。 学习如何有效地使用和操作您的 bash 历史记录将使您花更少的时间打字,而将更多的时间用于完成实际工作。 许多开发人员都熟悉 Don't Repeat Yourself 的 DRY 哲学。 有效利用 bash 的历史可以让你更接近这个原则,并且会加快你的工作流程。
先决条件
要遵循本指南,您需要访问运行基于 Linux 的操作系统的计算机。 这可以是您使用 SSH 连接到的虚拟专用服务器,也可以是您的本地计算机。 请注意,本教程使用运行 Ubuntu 20.04 的 Linux 服务器进行了验证,但给出的示例应该适用于运行任何版本的任何 Linux 发行版的计算机。
如果您打算使用远程服务器来遵循本指南,我们建议您先完成我们的 初始服务器设置指南 。 这样做将为您设置一个安全的服务器环境——包括一个具有 sudo
权限的非 root 用户和一个配置了 UFW 的防火墙——您可以使用它来培养您的 Linux 技能。
或者,您可以使用此页面上嵌入的交互式终端来试验本教程中的示例命令。 单击以下 Launch an Interactive Terminal! 按钮以打开终端窗口并开始使用 Linux (Ubuntu) 环境。
启动交互式终端!
设置历史默认值
在开始实际使用命令历史记录之前,调整一些 bash 设置以使其更有用可能会有所帮助。 这些步骤不是必需的,但它们可以更轻松地查找和执行您之前运行过的命令。
Bash 允许您调整它存储在历史记录中的命令数量。 它实际上有两个单独的选项:HISTFILESIZE
参数配置在历史文件中保留多少命令,而 HISTSIZE
控制当前会话存储在内存中的数量。
这意味着您可以为当前会话的内存中的历史大小设置一个合理的上限,并将更大的历史保存到磁盘中,以便以后检查。 默认情况下,bash 为这些选项设置非常保守的值,但您可以扩展这些值以利用更大的历史记录。 一些发行版已经增加了默认的 bash 历史设置,其值稍微大一些。
使用您喜欢的文本编辑器打开您的 ~/.bashrc
文件以更改这些设置。 在这里,我们将使用 nano
:
nano ~/.bashrc
搜索 HISTSIZE
和 HISTFILESIZE
参数。 如果已设置,请随意修改这些值。 如果这些参数不在您的文件中,请立即添加它们。 就本指南而言,将 10000 行保存到磁盘并将最后 5000 行加载到内存中可以正常工作。 这是对大多数系统的保守估计,但如果您注意到性能影响,可以向下调整这些数字:
~/.bashrc
. . . # for setting history length see HISTSIZE and HISTFILESIZE in bash(1) HISTSIZE=5000 HISTFILESIZE=10000 . . .
默认情况下,bash 在每个会话结束时写入其历史记录,并用更新的版本覆盖现有文件。 这意味着如果您使用多个 bash 会话登录,则只有最后一个退出的会话才会保存其历史记录。
您可以通过设置 histappend
设置来解决此问题,该设置将追加而不是覆盖历史记录。 这可能已经设置,但如果没有,您可以通过添加以下行来启用它:
~/.bashrc
. . . shopt -s histappend . . .
如果您想让 bash 立即将命令添加到您的历史记录而不是等待每个会话结束(以使一个终端中的命令立即在另一个终端中可用),您还可以设置或附加 history -a
命令添加到 PROMPT_COMMAND 参数,该参数包含在每个新命令提示符之前执行的命令。
要正确配置它,您需要自定义 bash 的 PROMPT_COMMAND
以更改命令在历史文件和当前 shell 会话内存中的记录方式:
- 首先,您需要立即使用
history -a
追加到历史文件。 - 接下来,您必须使用
history -c
清除 shell 会话中的当前历史记录。 - 最后,要将更新的历史记录加载回您的 shell 会话,请使用
history -r
命令。
将所有这些命令按顺序放在 PROMPT_COMMAND
shell 变量中将产生以下结果,您可以将其粘贴到 .bashrc
文件中:
~/.bashrc
. . . export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND" . . .
完成后,保存文件并退出。 如果您使用 nano
编辑了 .bashrc
文件,请按 CTRL + X
、Y
,然后按 ENTER
。
要实施您的更改,请注销并重新登录,或者通过运行 source
文件:
source ~/.bashrc
这样,您就调整了 shell 处理命令历史的方式。 您现在可以练习使用 history
命令查找以前的命令。
回顾你以前的 Bash 历史
查看 bash 历史记录的方法是使用 history
命令。 这将打印出我们最近的命令,每行一个命令。 这应该最多输出您为 HISTSIZE
变量选择的行数。 在这一点上可能会更少:
history
Output . . . 43 man bash 44 man fc 45 man bash 46 fc -l -10 47 history 48 ls -a 49 vim .bash_history 50 history 51 man history 52 history 10 53 history
history
返回的每个命令都与一个数字相关联,以便于参考。 本指南稍后将介绍这将如何发挥作用。
您可以通过在命令后指定一个数字来截断输出。 例如,如果您只想返回最后执行的 5 个命令,您可以键入:
history 5
Output 50 history 51 man history 52 history 10 53 history 54 history 5
要查找包含特定字符串的所有 history
命令,可以将结果通过管道传输到 grep
命令中,该命令在每一行中搜索给定字符串。 例如,您可以通过键入以下内容搜索具有 cd
的行:
history | grep cd
Output 33 cd Pictures/ 37 cd .. 39 cd Desktop/ 61 cd /usr/bin/ 68 cd 83 cd /etc/ 86 cd resolvconf/ 90 cd resolv.conf.d/
在许多情况下,能够检索您之前运行的命令列表会很有帮助。 如果您想再次运行其中一个命令,您的直觉可能是从输出中复制其中一个命令并将其粘贴到提示符中。 这可行,但 bash 带有许多快捷方式,可让您从历史记录中检索并自动执行命令。
从你的 Bash 历史执行命令
打印命令历史记录可能很有用,但除了作为参考之外,它并不能真正帮助您访问这些命令。
您可以调用并立即执行由 history
操作返回的任何命令,其编号前加感叹号 (!
)。 假设您的 history
结果与上一节的结果一致,您可以通过键入以下内容快速查看 history
命令的手册页:
!51
这将立即调用并执行与历史编号 51 关联的命令。
您还可以使用 !-n
语法执行相对于当前位置的命令,其中“n”替换为您要调用的先前命令的数量。
例如,假设您运行了以下两个命令:
ls /usr/share/common-licenses echo hello
如果您想调用并执行您在最近的命令之前运行的命令(ls
命令),您可以输入 !-2
:
!-2
要重新执行上次运行的命令,可以运行 !-1
。 但是,bash 提供了一个由两个感叹号组成的快捷方式,它将替换最近的命令并执行它:
!!
许多人在键入命令时使用它,但忘记了他们需要 sudo
权限才能执行它。 键入 sudo !!
将重新执行前面带有 sudo
的命令:
touch /etc/hello
Outputtouch: cannot touch `/etc/hello': Permission denied
sudo !!
Outputsudo touch /etc/hello [sudo] password for sammy:
这展示了此语法的另一个属性:这些快捷方式是纯替换,可以随意合并到其他命令中。
滚动浏览 Bash 历史
有几种方法可以滚动浏览 bash 历史记录,将每个连续的命令放在命令行上进行编辑。
最常见的方法是在命令提示符下按向上箭头键。 每多按一次向上箭头键,您就会进一步回到命令行历史记录。
如果你需要去另一个方向,向下箭头键以相反的方向遍历历史,最终带你回到你当前的空提示。
如果将手一直移动到箭头键似乎很麻烦,您可以使用 CTRL + P
组合在命令历史记录中向后移动,并使用 CTRL + N
组合在您的又是历史。
如果要跳回当前命令提示符,可以按 META + >
。 在大多数情况下,“元”键是 ALT
键,因此 META + >
将意味着按下 ALT + SHIFT + .
。 如果您发现自己在历史上很远并且想要回到空提示,这将很有用。
您也可以通过执行相反的操作并键入 META + <
转到命令历史记录的第一行。 这通常意味着按 ALT + SHIFT + ,
。
总而言之,这些是一些可用于滚动历史记录并跳转到任一端的键和组合键:
UP arrow key
:历史向后滚动CTRL + P
:历史向后滚动DOWN arrow key
:向前滚动历史CTRL + N
:向前滚动历史ALT + SHIFT + .
:跳转到历史的末尾(最近的)ALT+ SHIFT + ,
:跳转到历史起点(最远)
搜索 Bash 历史
尽管通过 grep
传递 history
命令可能是缩小结果范围的有用方法,但在许多情况下并不理想。
Bash 包括对其历史的搜索功能。 使用此功能的典型方法是使用 CTRL + R
组合键在历史记录中向后搜索(最先返回最近的结果)。
例如,您可以键入 CTRL + R
,然后开始键入上一个命令的一部分。 您只需键入部分命令。 如果它匹配不需要的命令,您可以再次按 CTRL + R
以获得下一个结果。
如果您不小心传递了您想要的命令,您可以通过键入 CTRL + S
向相反的方向移动。 如果您使用上一节中的键移动到历史记录中的不同点并希望向前搜索,这也很有用。
请注意,在许多终端中,CTRL + S
组合被映射为暂停终端会话。 这将拦截任何将 CTRL + S
传递给 bash 的尝试,并将“冻结”您的终端。 要取消冻结,请键入 CTRL + Q
以取消暂停会话。
大多数现代终端都不需要此挂起和恢复功能,您可以通过运行以下命令将其关闭而不会出现任何问题:
stty -ixon
stty
是一个实用程序,可让您从命令行更改终端的设置。 您可以将此 stty -ixon
命令添加到 ~/.bashrc
文件的末尾,以使此更改也永久生效。
如果您现在再次尝试使用 CTRL + S
进行搜索,它应该可以按预期工作以允许您向前搜索。
输入部分命令后搜索
一个常见的场景是输入命令的一部分,然后才意识到您之前已经执行过它并且可以搜索它的历史记录。
使用命令行中已有的内容进行搜索的正确方法是使用 CTRL + A
将光标移动到行首,使用 CTRL + R
调用反向历史记录,将当前行粘贴到用 CTRL + Y
搜索,然后再次使用 CTRL + R
反向搜索。
例如,假设您想更新 Ubuntu 系统上的包缓存。 您最近已经输入了这个,但是直到您再次在提示符中输入 sudo
之后才考虑到这一点:
sudo
此时,您意识到这是您在过去一天左右肯定完成的操作。 您可以按 CTRL + A
将光标移动到行首。 然后,按 CTRL + R
调用反向增量历史搜索。 这有一个副作用,即复制光标位置之后命令行上的所有内容并将其放入剪贴板。
接下来,按 CTRL + Y
将刚刚从命令行复制的命令段粘贴到搜索中。 最后,按 CTRL + R
在历史记录中向后移动,搜索包含您刚刚粘贴的内容的命令。
使用这样的快捷方式一开始可能看起来很乏味,但当你习惯它时它会非常有用。 当您发现自己已经输入了一半复杂命令并且知道您将需要历史记录来完成其余命令时,这将非常有帮助。
与其将它们视为单独的组合键,不如将它们视为单个复合操作可能会有所帮助。 您可以按住CTRL
键,然后依次按A
、R
、Y
,然后按R
键.
熟悉更高级的历史扩展
本指南已经涉及 bash 提供的一些最基本的历史扩展技术。 到目前为止,我们介绍的一些是:
!!
:展开到最后一条命令!n
:扩展历史编号为“n”的命令。!-n
:展开到历史中当前命令之前“n”个命令的命令。
事件指示符
以上三个例子是事件指示符的实例。 这些通常是使用某些标准调用先前历史命令的方法。 它们是您可用操作的选择部分。
例如,您可以通过键入以下内容来执行您运行的最后一个 ssh
命令:
!ssh
这将搜索命令历史记录中以 ssh
开头的行。 如果要搜索不在命令开头的字符串,可以用 ?
字符将其包围。 例如,要重复之前的 apt-cache search
命令,您可能会运行以下命令来查找并执行它:
!?search?
您可以尝试的另一个事件指示符涉及将最后一个命令中的字符串替换为另一个。 为此,请输入一个插入符号 (^
),后跟要替换的字符串,然后紧跟另一个插入符号、替换字符串和最后的插入符号。 不要包含任何空格,除非它们是您要替换的字符串的一部分或您要用作替换的字符串的一部分:
^original^replacement^
这将调用之前的命令(就像 !!
),在命令字符串中搜索 original
的实例,并将其替换为 replacement
。 然后它将使用替换字符串执行命令。
这对于处理诸如拼写错误之类的事情很有用。 例如,假设您在尝试读取 /etc/hosts
文件的内容时错误地运行了此命令:
cat /etc/hosst
Outputcat: /etc/hosst: No such file or directory
与其重写整个命令,不如运行以下命令:
^hosst^hosts^
这将修复上一个命令中的错误并成功执行。
词指示符
在事件指示符之后,您可以添加一个冒号 (:
),后跟一个 字指示符 来选择匹配命令的一部分。
它通过将命令分成“单词”来做到这一点,“单词”被定义为由空格分隔的任何块。 这使您有一些有趣的机会与您的命令参数进行交互。
单词编号从初始命令为“0”开始,第一个参数为“1”,并从那里继续。
例如,您可以列出目录的内容,然后决定要导航到同一目录。 您可以通过背靠背运行以下操作来做到这一点:
ls /usr/share/common-licenses cd !!:1
在这种情况下,您正在对最后一个命令进行操作,您可以通过删除第二个 !
以及冒号来缩短它:
cd !1
这将以相同的方式运行。
如果对您的目的有意义,您可以使用插入符号 (^
) 引用第一个参数,并使用美元符号 ($
) 引用最后一个参数。 这些在使用范围而不是特定数字时更有帮助。 例如,您可以通过三种方式将先前命令中的所有参数获取到新命令中:
!!:1* !!:1-$ !!:*
唯一的 *
扩展到除了初始命令之外的所有被调用命令的部分。 同样,您可以使用数字后跟 *
来表示应包含指定单词之后的所有内容。
修饰符
你可以做的另一件事来增加你正在回忆的历史行的行为是修改回忆的行为来操纵文本本身。 为此,您可以在扩展末尾的冒号 (:
) 字符后添加 modifiers。
例如,您可以使用 h
修饰符(它代表“head”)切断通向文件的路径,它会删除直到最后一个斜杠的路径(/
)特点。 请注意,如果您使用它来截断目录路径并且路径以斜杠结尾,则这不会按您希望的方式工作。
一个常见的用例是,如果您正在修改文件并意识到您想更改文件的目录以对相关文件进行操作。
例如,假设您运行此命令以将开源软件许可证的内容打印到您的输出中:
cat /usr/share/common-licenses/Apache-2.0
在对许可证适合您的需求感到满意后,您可能希望更改到它所在的目录。 您可以通过在参数链上调用 cd
命令并在最后切掉文件名来做到这一点:
cd !!:$:h
如果您运行 pwd
,它会打印您当前的工作目录,您会发现您已导航到上一个命令中包含的目录:
pwd
Output/usr/share/common-licenses
到达那里后,您可能需要再次打开该许可证文件以仔细检查,这次是在 less
之类的寻呼机中。
为此,您可以通过切断路径并仅使用带有 t
修饰符(代表“tail”)的文件名来执行与先前操作相反的操作。 您可以搜索上一次 cat
操作并使用 t
标志仅传递文件名:
less !cat:$:t
您可以轻松地保留完整的绝对路径名,并且此命令在这种情况下可以正常工作。 但是,可能在其他时候情况并非如此。 例如,您可以使用相对路径查看嵌套在当前工作目录下的几个子目录中的文件,然后使用“h”修饰符切换到子目录。 在这种情况下,您将无法再依赖相对路径名来访问文件。
另一个非常有用的修饰符是 r
修饰符,它去除了尾随扩展。 如果您使用 tar
提取文件并希望在之后更改到生成的目录,这可能很有用。 假设生成的目录与文件同名,您可以执行以下操作:
tar xzvf long-project-name.tgz cd !!:$:r
如果你的 tarball 使用 tar.gz
扩展而不是 tgz
,你可以只传递两次修饰符:
tar xzvf long-project-name.tgz cd !!:$:r:r
一个类似的修饰符 e
删除了除了尾随扩展名之外的所有内容。
如果您不想执行您正在调用的命令而只想找到它,您可以使用 p
修饰符让 bash 回显命令而不是执行它。
如果您不确定您是否选择了正确的作品,这将非常有用。 这不仅会打印它,而且如果您想修改它,还会将其放入您的历史记录中以供进一步编辑。
例如,假设您在主目录上运行了 find
命令,然后意识到您想从根 (/
) 目录运行它。 您可以像这样检查您是否进行了正确的替换(假设原始命令与您的历史记录中的数字 119 相关联):
find ~ -name "file1" !119:0:p / !119:2*:p
Outputfind / -name "file1"
如果返回的命令正确,您可以使用CTRL + P
组合键执行。
您还可以使用 s/original/new/
语法在命令中进行替换。
例如,您可以通过键入:
!119:s/~/\//
这将替换搜索模式的第一个实例 (~
)。
您还可以通过将 g
标志与 s
一起传递来替换每个匹配项。 例如,如果要创建名为 file1
、file2
和 file3
的文件,然后要创建名为 dir1
、[ 的目录X143X]、dir3
,你可以这样做:
touch file1 file2 file3 mkdir !!:*:gs/file/dir/
当然,在这种情况下只运行 mkdir dir1 dir2 dir3
可能更直观。 但是,当您习惯使用修饰符和其他 bash 历史扩展工具时,您可以在命令行上大大扩展您的功能和生产力。
结论
通过阅读本指南,您现在应该对如何利用可用的历史操作有了一个很好的了解。 其中一些可能会比其他的更有用,但很高兴知道 bash 具有这些功能,以防您发现自己处于可以帮助挖掘它们的位置。
如果不出意外,单独的 history
命令、反向搜索和基本的历史扩展可以帮助您加快工作流程。