如何在 Linux VPS 上使用 Bash 历史命令和扩展
介绍
在服务器环境中工作时,您将花费大量时间在命令行上。您很可能会使用 bash shell,这是大多数发行版的默认设置。
在终端会话期间,您可能会经常重复某些命令,并且会更频繁地键入这些命令的变体。虽然在开始时重复输入每个命令可能是一种很好的做法,但在某些时候,它会越界,变得具有破坏性和烦恼。
幸运的是,bash shell 有一些相当完善的历史记录功能。学习如何有效地使用和操纵您的 bash 历史将使您减少打字时间,而将更多时间用于完成实际工作。许多开发人员都熟悉DRY不要重复自己的理念。有效地使用 bash 的历史可以让你更接近这个原则并加快你的工作流程。
先决条件
要按照本指南进行操作,您需要访问运行基于 Linux 的操作系统的计算机。这可以是您使用 SSH 连接到的虚拟专用服务器,也可以是您的本地计算机。请注意,本教程是使用运行 Ubuntu 20.04 的 Linux 服务器进行验证的,但给出的示例应该适用于运行任何版本的任何 Linux 发行版的计算机。
如果您计划使用远程服务器来遵循本指南,我们鼓励您首先完成我们的初始服务器设置指南。这样做将为您设置一个安全的服务器环境 — 包括具有 sudo
权限的非根用户和配置有 UFW 的防火墙 — 您可以使用它来培养您的 Linux 技能。
设置历史默认值
在开始实际使用命令历史记录之前,调整一些 bash 设置以使其更有用可能会对您有所帮助。这些步骤不是必需的,但它们可以让您更轻松地查找和执行您之前运行过的命令。
Bash 允许您调整它在历史记录中存储的命令数量。它实际上有两个单独的选项:HISTFILESIZE
参数配置历史文件中保留的命令数量,而 HISTSIZE
控制当前会话在内存中存储的数量.
这意味着您可以为当前会话的内存中的历史记录大小设置一个合理的上限,并将更大的历史记录保存到磁盘以供您稍后检查。默认情况下,bash 为这些选项设置非常保守的值,但您可以扩展这些值以利用更大的历史记录。一些发行版已经增加了默认的 bash 历史设置,其值稍微大一些。
使用您喜欢的文本编辑器打开您的 ~/.bashrc
文件以更改这些设置。在这里,我们将使用 nano
:
- nano ~/.bashrc
搜索 HISTSIZE
和 HISTFILESIZE
参数。如果已设置,请随意修改值。如果这些参数不在您的文件中,请立即添加。出于本指南的目的,将 10000 行保存到磁盘并将最后 5000 行加载到内存中就可以了。这是对大多数系统的保守估计,但如果您发现性能受到影响,可以将这些数字调低:
. . .
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=5000
HISTFILESIZE=10000
. . .
默认情况下,bash 在每个会话结束时写入其历史记录,用更新版本覆盖现有文件。这意味着如果您使用多个 bash 会话登录,则只有最后一个退出的会话会保存其历史记录。
您可以通过设置 histappend
设置来解决此问题,该设置将追加而不是覆盖历史记录。这可能已经设置,但如果没有,您可以通过添加以下行来启用它:
. . .
shopt -s histappend
. . .
如果你想让 bash 立即将命令添加到你的历史记录中,而不是等待每个会话结束(使一个终端中的命令立即在另一个终端中可用),你还可以设置或附加 history -a
命令添加到 PROMPT_COMMAND
参数,该参数包含在每个新命令提示符之前执行的命令。
要正确配置它,您需要自定义 bash 的 PROMPT_COMMAND
以更改命令在历史文件和当前 shell 会话内存中的记录方式:
- 首先,您需要使用
history -a
立即追加到历史文件。 - 接下来,您必须使用
history -c
清除 shell 会话中的当前历史记录。 - 最后,要将更新的历史记录加载回您的 shell 会话,请使用
history -r
命令。
将所有这些命令按顺序放在 PROMPT_COMMAND
shell 变量中将产生以下结果,您可以将其粘贴到 .bashrc
文件中:
. . .
export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
. . .
完成后,保存文件并退出。如果您使用 nano
编辑了您的 .bashrc
文件,请按 CTRL + X
、Y
,然后<代码>输入。
要实施您的更改,请注销并重新登录,或者通过运行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 + >
来实现。在大多数情况下,\meta 键是 ALT
键,因此 META + >
意味着按下 ALT + SHIFT + .
。这如果您发现自己回溯到很早的历史记录并想返回到空白提示符,这将很有用。
您还可以通过执行相反的操作并键入 META + <
来转到命令历史记录的第一行。这通常意味着按 ALT + SHIFT + ,
。
总而言之,这些是您可以用来滚动历史记录并跳转到任一端的一些键和组合键:
向上箭头键
:在历史记录中向后滚动CTRL + P
:在历史中向后滚动向下箭头键
:在历史记录中向前滚动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
的实例,并将其替换为 替换
。然后它将使用替换字符串执行命令。
这对于处理拼写错误之类的事情很有用。例如,假设您在尝试读取 /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-$
- !!:*
单独的 *
扩展到除初始命令之外的被调用命令的所有部分。同样,您可以使用一个数字后跟 *
来表示应该包括指定单词之后的所有内容。
修饰符
您可以做的另一件事来增强正在调用的历史行的行为,即修改调用的行为以操纵文本本身。为此,您可以在扩展末尾的冒号 (:
) 字符后添加 修饰符。
例如,您可以使用 h
修饰符(它代表 \head)截断通向文件的路径,它会删除直到最后一个斜杠 (/
) 字符。请注意,如果您使用它来截断目录路径并且路径以尾部斜杠结尾,这将无法按照您希望的方式工作。
一个常见的用例是,如果您正在修改文件并意识到您想更改到文件的目录以对相关文件进行操作。
例如,假设您运行此命令将开源软件许可证的内容打印到您的输出中:
- cat /usr/share/common-licenses/Apache-2.0
在对许可证满足您的需要感到满意之后,您可能想要切换到它所在的目录。您可以通过在参数链上调用 cd
命令并在末尾截断文件名来执行此操作:
- cd !!:$:h
如果你运行 pwd
,它会打印你当前的工作目录,你会发现你已经导航到上一个命令中包含的目录:
- pwd
Output/usr/share/common-licenses
到达那里后,您可能想再次打开该许可证文件进行仔细检查,这次是在 less
之类的寻呼机中。
为此,您可以通过截断路径并仅使用带有 t
修饰符(代表“尾巴”)的文件名来执行与之前操作相反的操作。您可以搜索最后一个 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
的目录、dir2
、dir3
,你可以这样做:
- touch file1 file2 file3
- mkdir !!:*:gs/file/dir/
当然,在这种情况下,只运行 mkdir dir1 dir2 dir3
可能更直观。然而,当您习惯使用修饰符和其他 bash 历史扩展工具时,您可以极大地扩展您在命令行上的能力和生产力。
结论
通过阅读本指南,您现在应该对如何利用可用的历史操作有了很好的了解。其中一些可能比其他的更有用,但最好知道 bash 具有这些功能,以防您发现自己处于挖掘它们会有所帮助的位置。
如果不出意外,单独的 history
命令、反向搜索和基本的历史扩展可以帮助您加快工作流程。