Linux命令行下 " ! " 的几个用法

! 在大多数编程语言中表示取反的意思,但是在命令行中,他还有一些其他的神奇用法。熟练掌握这些用法,可以大大提高我们日常命令行操作的效率。

1 执行历史命令

!!

! 在命令行中可以用来执行历史命令,最常用的,大家应该比较熟悉的是执行上一条命令 !!,它可以用来执行最近的一条命令

该命令在我们忘记使用 root 权限执行某项命令时很有用:sudo !! 在上一条命令之前加 root 权限再执行。

比如,我们要用 fdisk 命令查看磁盘信息,但是如果没有 root 权限是会被拒绝的,这时我们就可以直接 sudo !!

$ fdisk -l
fdisk: cannot open /dev/loop0: Permission denied
# ...
fdisk: cannot open /dev/loop30: Permission denied
fdisk: cannot open /dev/loop31: Permission denied
$ sudo !!
sudo fdisk -l
[sudo] password for song:
Disk /dev/loop0: 4.2 MiB, 4448256 bytes, 8688 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
# ...

或者在我们忘记加某项参数时,也可以通过 !! -p 来快速添加参数执行上一条命令。比如我要 gdb 调试某个源文件,但是忘了加 -g 参数,即可:

g++ quickSort.cpp
!! -g

有趣的是,!! 符号可以理解为完整地重复上一条指令的文本,即使在有些时候语义不连贯也是可行的。比如下面这个情景。

在当前目录下,我们有 tsne.py 和 tsne.png 两个文件,这时我想要 vim 修改 tsne.py 的内容,很有可能会手快通过 tab 键执行这样的命令:vim tsne.p,这时我们可以直接 !!y,就能顺利地执行 vim tsne.py

!n

!! 命令虽然很好用,但是它只能执行上面最近一条命令,那么能不能执行上面几条(如倒数第二条、倒数第三条)指令呢,又能不能有一个一般的命令,来执行某一条历史命令呢?当然是有的!

首先,我们知道 history 命令可以查看最近的历史命令。值得注意的是,它显示的每条历史命令之前会有一个编号,我们好像在平常没太注意过这个编号有什么作用:

$ history
 2083  ls
 2084  vim quickSort.cpp
 2085  g++ quickSort.cpp
 2086  g++ quickSort.cpp  -g
 2087  history

实际上,我们可以直接通过这个编号来执行某条历史命令,如 !2084

这个用法好像比较鸡肋,因为历史命令编号我们很少回去记住它,每次要用 history 命令查,再执行的话未免有些麻烦。笔者一时能想到的应用场景是如果我们在一段时间(比如一小时、一下午)内,要重复的用某条超复杂的命令(比如很长的路径名),我们不妨短时记忆一下某个历史命令编号,并多次使用该编号执行历史命令。

比如我今天下午调试程序时要多次修改某个 py 文件,就可以记住这个命令的编号,然后每次 !1994 来执行历史命令。

 1994  vim /home/ps/JJ_Projects/ssl_transformer_aes/my_project/SiT/pretrain_ssl.sh

!-i

!-i 的形式可以执行倒数第 i 条命令。如 !-6!-8等。特别地,!-1 就相当于 !!

!cmd

!cmd 通过关键词来执行历史命令。可以按照下面的命令来理解:

$ ls /home > /dev/null
$ ls -l /home/song/JJ_Projects/ > /dev/null
$ ls -la /home/song/JJ_Projects/ > /dev/null
$ ls -lA /usr/bin > /dev/null

上面是相同的ls命令对应了不同参数和文件夹。此外我们将每一个标准输出都传递到了 /dev/null 因为我们并不希望处理程序的标准输出。现在我们可以调用命令的关键词来实现它们。

$ !ls
$ !ls -l
$ !ls -la
$ !ls -lA

当你使用 !ls 关键词来执行之前命令的时候,你一定会被标准输出给惊讶到。

2 复用历史参数

!$和!^

如同 !! 来执行上一条命令一样,!$!^ 也是很常用的,它们的作用是重复上一条命令的第一个或最后一个参数

!$ 为例,考虑这样的场景,我要删除某个目录下的所有 png 图像文件,但是在删除之前,我要先查看一下,确定这些图像文件确实都是没有用的。可以这样操作:

ls /home/song/JJ_Projects/ava-mlsp/metadata/*.png
rm !$

这样能省去我们重复上一个命令操作参数的时间。

另一个更普遍的场景:当我们编辑完 ~/.bashrc 文件后,需要用 source 命令使它生效,此时可以:

vim ~/.bashrc
# 一顿操作,修改 .bashrc 文件内容
source !$

!cmd:n

同样的,我们将 !$!^ 推广到一般情况,!cmd:n :获取最近一次 cmd 命令的第 n 个参数(参数的个数从 0 开始计)。

如:

ls -a -l
ls !ls:1 	

这样后面一条命令相当于执行了:ls -l

3 取反

! 在很多编程语言中都是取反的意思,!= 也通常都是不等于的意思。

在逻辑判断中取反

同大多数编程语言一样,! 在 shell 脚本中表示取反的意思。

[ ! -d /home/song/JJ_Prjects ] 可以用来判断该目录是否为空。

在命令中取反

rm !(train.py) 可以删除当前目录下除了 train.py 之外的全部文件。rm !(*.png) 删除当前目录下除了后缀名为 png 之外的全部文件。

总结起来最常用的除了在编程时取反之外,在命令行中用起来比较丝滑的也就是 !!!$,它们的推广的更一般的形式虽然能实现的功能更全面,但稍显麻烦,不太常用。以上就是笔者对 ! 在命令行中使用的总结了,如果有错误或补充,欢迎留言讨论。

Ref:

https://linuxstory.org/mysterious-ten-operator-in-linux/

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐