7 个你真正会用到的 Bash 历史命令快捷方式

通过这些必要的 Bash 快捷方式节省命令行时间。
535 位读者喜欢这个。
Command line prompt

Opensource.com

大多数 Bash 历史命令快捷方式指南都详尽地列出了所有可用的快捷方式。问题在于,我会使用一次快捷方式,然后在尝试所有可能性时变得茫然。然后我会继续我的工作日,完全忘记它们,只保留了我刚开始使用 Bash 时学到的众所周知的 !! 技巧

所以它们中的大多数从未被记住。

本文概述了我每天实际使用的快捷方式。它基于我的书《Learn Bash the hard way》的部分内容;(您可以阅读预览来了解更多信息)。

当人们看到我使用这些快捷方式时,他们经常问我:“你刚才做了什么!?” 这只需要最少的努力或智力,但要真正学会它们,我建议每天使用一个,持续一周,然后再学习下一个。花时间让它们在你的手指下熟练掌握是值得的,因为从长远来看,你节省的时间将是巨大的。

1. “最后一个参数”快捷方式:!$

如果你只从本文中学习一个快捷方式,那就学习这个。它将最后一条命令的最后一个参数替换到你的行中。

考虑以下场景

$ mv /path/to/wrongfile /some/other/place
mv: cannot stat '/path/to/wrongfile': No such file or directory

啊,我在命令中放了wrongfile文件名。我应该放 rightfile 才对。

你可能会决定重新输入最后一条命令,并将 wrongfile 完全替换为 rightfile。相反,你可以输入

$ mv /path/to/rightfile !$
mv /path/to/rightfile /some/other/place

命令就会工作。

在 Bash 中,还有其他方法可以通过快捷方式实现相同的效果,但重用最后一条命令的最后一个参数的技巧是我最常用的技巧之一。

2. “第n个参数”快捷方式:!:2

有没有做过类似的事情?

$ tar -cvf afolder afolder.tar
tar: failed to open

像许多其他人一样,我经常搞错 tar(和 ln)的参数,次数多到我都不想承认。

xkcd comic

当你像那样混淆参数时,你可以运行

$ !:0 !:1 !:3 !:2
tar -cvf afolder.tar afolder

你的声誉就会被挽救。

最后一条命令的项目是零索引的,可以用 !: 后的数字替换。

显然,你也可以使用它来重用最后一条命令中的特定参数,而不是全部参数。

3. “所有参数”快捷方式:!:1-$

想象一下,我运行一个像这样的命令

$ grep '(ping|pong)' afile

参数是正确的;但是,我想在文件中匹配 pingpong,但我使用了 grep 而不是 egrep

我开始输入 egrep,但我不想重新输入其他参数。所以我可以使用 !:1$ 快捷方式来请求前一条命令的所有参数,从第二个参数(记住它们是零索引的)到最后一个参数(用 $ 符号表示)。

$ egrep !:1-$
egrep '(ping|pong)' afile
ping

你不必选择 1-$;你可以选择一个子集,例如 1-23-9(如果前一条命令中有那么多参数)。

4. “倒数第n个”快捷方式:!-2:$

当我知道如何立即纠正我的最后一条命令时,上面的快捷方式非常棒,但通常我在原始命令之后运行命令,这意味着最后一条命令不再是我想要引用的命令。

例如,使用之前的 mv 示例,如果我在犯错后使用 ls 检查文件夹的内容

$ mv /path/to/wrongfile /some/other/place
mv: cannot stat '/path/to/wrongfile': No such file or directory
$ ls /path/to/
rightfile

我就不能再使用 !$ 快捷方式了。

在这些情况下,我可以在 ! 后插入 -n: (其中 n 是历史记录中要回溯的命令数),以从较旧的命令中获取最后一个参数

$ mv /path/to/rightfile !-2:$
mv /path/to/rightfile /some/other/place

同样,一旦你学会了它,你可能会惊讶于你有多频繁地需要它。

5. “获取文件夹”快捷方式:!$:h

这个快捷方式表面上看起来不太有前途,但我每天都会使用它数十次。

想象一下,我运行一个像这样的命令

$ tar -cvf system.tar /etc/system
 tar: /etc/system: Cannot stat: No such file or directory
 tar: Error exit delayed from previous errors. 

我可能想做的第一件事是进入 /etc 文件夹,看看里面有什么,并弄清楚我做错了什么。

我可以通过以下方式一蹴而就地做到这一点

$ cd !$:h
cd /etc

这个快捷方式的意思是:“获取最后一条命令的最后一个参数 (/etc/system),并去掉它的最后一个文件名组件,只留下 /etc。”

6. “当前行”快捷方式:!#:1

多年来,我偶尔会想,我是否可以引用当前行上的参数,直到最终查阅并学习它。我真希望我早点这样做。我最常使用它来制作备份文件

$ cp /path/to/some/file !#:1.bak
cp /path/to/some/file /path/to/some/file.bak

一旦熟练掌握,它就可以成为 … 的非常快速的替代方案

7. “搜索和替换”快捷方式:!!:gs

这个快捷方式在引用的命令中搜索,并将前两个 / 字符中的内容替换为后两个字符中的内容。

假设我想告诉全世界我的 s 键坏了,输出的是 f 而不是 s

$ echo my f key doef not work
my f key doef not work

然后我意识到我只是不小心按到了 f 键。要将所有 f 替换为 s,我可以输入

$ !!:gs/f /s /
echo my s key does not work
my s key does not work

它不仅适用于单个字符;我也可以替换单词或句子

$ !!:gs/does/did/
echo my s key did not work
my s key did not work

测试一下

只是为了向你展示如何组合这些快捷方式,你能弄清楚这些脚趾甲屑会输出什么吗?

$ ping !#:0:gs/i/o
$ vi /tmp/!:0.txt
$ ls !$:h
$ cd !-2:h
$ touch !$!-3:$ !! !$.txt
$ cat !:1-$

结论

对于日常命令行用户来说,Bash 可以是一个优雅的快捷方式来源。虽然有成千上万的技巧和窍门可以学习,但这些是我经常使用的最喜欢的技巧和窍门。

如果你想更深入地了解 Bash 可以教给你的所有知识,请购买我的书《Learn Bash the hard way》或查看我的在线课程《Master the Bash shell》。


本文最初发布在 Ian 的博客 Zwischenzugs.com 上,并已获得许可重复使用。

接下来阅读什么
标签
Avatar
开源编码员/演讲者/作者/博主/培训师

12 条评论

哇!真的很有用!感谢分享。

哇,这太棒了。我知道第一个,但其他六个将为我节省很多在低声咒骂的时间!

你的“当前行”示例太牵强了。你的示例是将备份复制成这样

$ cp /path/to/some/file !#:1.bak

但更好的写法是使用文件名生成

$ cp /path/to/some/file{,.bak}

但这并不是历史扩展……我不确定我是否能想出一个使用 `!#:1` 的好理由。

我使用 bash 大约 19 年了,除了 !!$ 之外,我从未使用过这些。用户最好学习如何使用向上箭头和触发 edit-and-execute-command 的键序列来编辑他们之前的命令行。你将获得一个 emacs 或 vi 编辑器,它可以让你非常快速地更改参数。

对我来说,bash 历史快捷方式的问题是……我从来没有需要学习它们。

假设你的 shell 启用了 readline,我发现使用箭头键和修饰键来浏览历史记录比输入 !:1(或记住它的含义)容易得多。

示例

Ctrl+R 进行反向搜索
Ctrl+A 移动到行首(Home 键也可以)
Ctrl+E 移动到行尾(End 键也可以)
Ctrl+K 杀死(删除)从光标到行尾的文本
Ctrl+U 杀死从光标到行首的文本
Alt+F 向前移动一个单词(Ctrl+右箭头也可以)
Alt+B 向后移动一个单词(Ctrl+左箭头也可以)
等等。

YMMV 当然。

这些是一些很棒的历史记录替换技巧。我知道其中一些,但不是全部。

```
cp /path/to/some/file !#:1.bak
```

我认为使用花括号扩展会更容易实现这个示例,像这样

```
cp /path/to/some/{file,file.bak}
```

很棒的技巧!:0 非常感谢!!

很棒的文章,但它缺少一个我经常使用的模式,!* 它重复所有
前一个命令中的参数。

一个用例是在运行 rm 之前运行 ls 以确保匹配所有正确的文件。
例如,删除所有 pdf 文件,这些文件是
教程...

ls *[Tt]ut*.pdf
rm !*

关于第 3 项
$ grep '(ping|pong)' afile

你不必输入正确的命令(egrep,而不是 grep),因为如果你在你的范围内包含 0,那么只需 "e" 就可以工作

$ e!:0-2
egrep '(ping|pong)' afile
pingpang
pangpong
pingpong

关于第 3 项
$ grep '(ping|pong)' afile

你不必输入正确的命令(egrep,而不是错误输入的 grep),因为如果你在你的范围内包含 0,那么只需 "e" 就可以工作

$ e!:0-2
egrep '(ping|pong)' afile
pingpang
pangpong
pingpong

Creative Commons License本作品根据 知识共享许可协议 授权。署名-相同方式共享 4.0 国际许可协议。
© . All rights reserved.