交互式shell中的感叹号(!)
1. 概述
如果我们经常使用交互式 Linux 命令行,我们可能会遇到感叹号。在大多数 shell 上,此符号用于通过历史扩展重新运行以前执行的命令,从而提高我们的工作效率。
在本教程中,我们将讨论“!”的几个用例。从我们的 shell 历史中重新运行命令的符号。
2. shell历史扩展
最流行的 shell,如bash、csh和zsh可以存储我们之前在 shell 中执行的命令的历史记录。这些命令逐行存储在历史文件中——通常位于我们的主目录中。
**当我们输入“!argument”时,shell 会尝试扩展该命令,将其替换为对应于参数的先前命令。**如果没有匹配,shell 会抱怨并返回代码 1。当我们处理输入长命令时,这会非常方便。
默认情况下,此功能已在流行的 shell 上启用。但是,如果您不喜欢它,可以使用set +H禁用它。
让我们看看“!”的魔力行动中的象征。
2.1. 重复最后一个命令 (!!)
通常,作为UP和Enter组合的替代,我们可以输入“!!” 重复最近的命令:
$ echo "Hello, World"
Hello, World
$ !!
echo "Hello, World"
Hello, World
例如,如果我们想杀死一个程序的多个实例,我们可以输入一次pkill 命令,然后用“!!”重复它:
$ pkill node
$ !!
pkill node
2.2. 按数字重复命令 (!n)
众所周知,shell 在纯文本文件中逐行跟踪其历史记录。我们可以通过将历史文件中的行号指定为“!”的参数来重新运行某个命令。
让我们打印bash历史文件中最早的(前)五个命令:
$ cat ~/.bash_history -b | head -n5
1 nvpk
2 sudo pacman -Ss pipewire
3 sudo pacman -Ss xdg-desktop
4 install wofi wofi-calc wofi-emoji-git
5 shutdown -h 60
cat 的*-b*选项会将行号添加到行前。我们可以使用这些信息重复历史文件中的第五条命令:
$ !5
shutdown -h 60
Shutdown scheduled for Sun 2022-05-10 02:07:31 PKT, use 'shutdown -c' to cancel.
**只要我们不清除 shell 历史记录,每个命令的行号就不会改变。**因此,我们可以通过记住文件中的行号来重复一个长命令(需要时间但节省时间)。
同样,我们也可以为“!”提供一个负数 重新运行历史文件中的前n 个命令:
$ cat ~/.bash_history -b | tail -n5
96 nvim ./set-bg
97 nvim ../../.config/sway/config
98 ./set-bg
99 grep Verbose /etc/pacman.conf
100 sudo pacman -Ss wofi
$ !-2
grep Verbose /etc/pacman.conf
#VerbosePkgLists
2.3. 使用模式重复命令 (!pattern)
在历史文件中搜索先前命令的行号的麻烦可能很耗时。幸运的是,我们可以输入“!” 符号后跟一个模式以执行与该模式匹配的上一个命令。
例如,让我们执行最后一个涉及grepping 的命令:
$ !grep
grep Verbose /etc/pacman.conf
#VerbosePkgLists
**但是,我们应该知道它只需要起始模式。**以下将不起作用:
$ !pacman.conf
bash: !pacman.conf: event not found
2.4. 在上一个命令中重用文件路径 (!$)
有时,我们可能不需要重复整个命令,而只需要前一个命令的一部分。幸运的是,“!” 我们是否也涵盖了这方面的内容。
例如,如果我们想重用前面命令中提供的文件路径,我们可以使用“!$”:
$ cat ~/.bash_history
1 nvpk
2 sudo pacman -Ss pipewire
3 sudo pacman -Ss xdg-desktop
4 install wofi wofi-calc wofi-emoji-git
5 shutdown -h 60
.
.
.
$ file $?
file ~/.bash_history
/home/hey/.bash_history: ASCII text
2.5. 重用前一个命令的参数 (!*)
我们还可以重用上一个命令的参数。考虑一个创建一堆文本文件的示例:
$ touch index.html index.css index.js
现在,如果我们想删除index.js文件,我们可以简单地使用“!:3”引用该参数:
$ rm !:3
同样,我们可以使用“!:nm”表示法指定一系列参数:
$ touch style.js head.css foot.css side.css
$ mv !:1-3 style/
该命令会将head.css、foot.css和side .css文件移动到*style/*目录。 此外,我们还可以使用带有“!*”的先前命令的所有参数。
2.6. sudo命令(sudo!!)
我们也可以通过在“!”前输入sudo来执行前面需要 root 权限的命令。符号:
$ mount /dev/sda3 /mnt
mount: /mnt: must be superuser to use mount.
$ sudo !!
3. 替代方案:搜索 Shell 历史
回到过去,交互式 shell 中没有实现快捷方式。所以 ”!” symbol 是 Linux 用户重复上一个命令的首选工具。 随着时间的推移,shell 变得非常先进,并引入了许多有效的方式来使用交互式命令行。其中一项功能包括使用CTRL+R搜索 shell 历史记录:
$ grep Verbose /etc/pacman.conf
bck-i-search: gre|
当我们键入 CTRL+R 时,shell 会提示我们输入一个模式。当我们键入时,shell 将显示一个与我们键入的模式非常匹配的命令。因此,找出我们想要执行的内容是反应性和快速的。
如果给定模式有多个结果,我们可以再次按CTRL+R来循环匹配。
因此,简而言之,我们最好使用这种方法,而不是使用历史扩展功能来重复最后的命令。但是,如果我们需要前一个命令的一部分,我们总是可以使用历史扩展。