本文完整阅读约需 13 分钟,如时间较长请考虑收藏后慢慢阅读~

Linux下的命令行Shell由于其历史原因(需要兼容没有鼠标的设备如电传打字机),往往并不自带鼠标定位光标支持,这就造成一个比较麻烦的问题:如果我写了一个非常复杂的命令,想要修改里面的部分内容,再执行一次,就需要一直按下键盘上的方向键,找到想要替换的内容,狂按 backspace 删除,再回车执行。这一过程费时费力,且在服务器管理等存在延迟的情况下经常出现方向键按太多,又得按回去的问题。本文将为读者介绍在zsh中安装并配置 mouse.zsh 插件,使终端支持鼠标定位,提升命令行操作效率的小技巧,以及综合应用各种方法的最佳实践。

0x01 现存方案1——快捷键大法

如果你是Shell/Emacs重度用户,看完引言后,你也许会回答Ctrl+A / Ctrl+E 大法好,甚至可能会祭出 Meta+F / Meta+B 绝招,可这些快捷键往往还是无法精准定位到所需位置,依旧没能摆脱重复操作。

的确,部分Shell或窗口管理器如Bash/Tmux支持Vim模式,允许用户使用Vim语法来进行替换/重复/搜索等操作,然而这一过程依旧无法摆脱重复的击键、陡峭的学习曲线与繁琐的记忆。

0x02 现存方案2——sed替换

快捷键的复杂性无法避免,但其实还有一种巧妙的方法可以在不输入快捷键的情况下对上一次输入的命令进行替换,变相满足我们的需求。

假设这里有一个命令:

./server.sh --local-port=8008 --remote-port=1233 --name="server-8008-1233" --quiet --daemon --no-restart --enable-compression --log="./server-8008-1233.log"

我们输入了这个命令,结果发现local-port应该是8088而非8008,这时我们无论是通过方向键还是通过快捷键都很难便捷地一次性将8008修改为8088。但如果使用Bash内建的 ! 语法配合sed,这一操作将会变得非常简单:

!:s/8008/8088/g

这时候Bash会另起一行,并立即将上次命令中的8008替换为8088。

如果你的命令并不在上一次输入,同样不用担心,! 语法提供了丰富的参数可选,这里简单列举两项:

  1. 通过索引定位
!-3:s/8008/8088/g

定位前面倒数第三次输入的命令

  1. 通过前缀定位
!./server:s/8008/8088/g

定位最近一次输入前缀为 ./server 的命令

全部内容可参考 GNU Bash的文档:https://www.gnu.org/software/bash/manual/html_node/History-Interaction.html#History-Interaction

这种方法尽管巧妙,但其替换的本质依旧存在局限性,有没有更好的办法能帮助我们快速定位呢?

0x03 使用鼠标快速定位

其实在鼠标发明之初是并没有指针的,当时的『鼠标指针』只是命令行界面中一个闪烁的光标,操作系统允许用户通过移动鼠标的方式来快速移动光标(如DOS中的命令行鼠标),实现更高效的操作。

随着GUI的普及,我们逐渐形成了『鼠标是为图形界面服务』的思维定式,但如果追溯过往,其实会发现并非如此。正如编辑文本时使用鼠标定位光标更方便,在命令行操作过程中使用鼠标进行定位也是提升操作效率最简单、最直接的方式。

比较可惜的是,大多数终端模拟器或Shell并没有自带鼠标支持,但如果读者们使用的是zsh,有一个很好用的插件 mouse.zsh 可以为zsh新增鼠标支持,安装方法如下:

wget http://stchaz.free.fr/mouse.zsh -O /usr/bin/mouse.zsh
echo ". /usr/bin/mouse.zsh" >> ~/.zshrc
echo "bindkey -M emacs '\em' zle-toggle-mouse" >> ~/.zshrc
source ~/.zshrc

这时我们按下 esc & m (不是同时按,是先按 esc 再按 m) 就会进入鼠标模式(模拟VT200终端),这时终端就已支持使用鼠标定位,我们可以在命令的任意位置点击鼠标左键进行定位,然后按下Ctrl+W删除光标前的单词,最后再按一次 esc & m 退出鼠标模式。这里笔者再举一个例子:

./server.sh --no-restrat --silent

假设笔者不小心把 --no-restart 输入成了 --no-restrat,这时我们按下 esc & m ,拿起鼠标点击 --no-restrat 后的空格,并按下 Ctrl+W 删除这个参数。

./server.sh  --silent

接下来我们输入正确的参数:

./server.sh --no-restart --silent

再按下 esc & m ,退出鼠标选择模式,这时我们就使用鼠标成功编辑了这一命令。

这里有几点需要注意:

  1. 如果觉得 esc & m 还是很累(毕竟要按两个键),可以将安装命令中的 echo "bindkey -M emacs '\em' zle-toggle-mouse" >> ~/.zshrc 替换为 echo "zle-toggle-mouse" >> ~/.zshrc,但缺点是在部分不支持独立编辑模式的终端下无法正常使用鼠标进行框选。

  2. 如果你使用的是MacOS自带 Terminal.app ,发现依旧无法使用鼠标进行定位,请开启自带终端的鼠标报告功能(快捷键:Command + R)。

(方法2可以配合方法1使用,利用鼠标报告功能的开关来区分框选与鼠标选择模式)

  1. \em代表 esc & m,这是ECMA-48所规定的转义序列表,你也可以使用满足ECMA-48规则的其他快捷键来触发鼠标选择模式。

0x04 最佳实践

上面介绍了纯命令行的不便之处,也介绍了使用鼠标进行快速定位的方法,但如果想问什么方法更高效,其实是没有一个固定规则的,不同的方法各自有其适用之处,笔者个人针对以上三种编辑方法总结了一些最佳实践:

1. 使用快捷键的场景

  • 需要编辑的内容在最开始(比如需要加一个sudo)
  • 需要立刻跳转本行到最开始或最后
  • 在本行最开始,想要删除整行内容
  • 需要删除某个单词或参数

2. 使用sed替换的场景

  • 需要重复旧命令,但新命令存在有规律的变化
  • 简单的Typo(如输错参数)

3. 使用鼠标定位的场景

  • 需要重复旧命令,但新命令变化较多/较复杂
  • 复杂的Typo(如漏掉关键参数等)

在读者实际操作过程中所面临的情况可能比本文所描述或总结得更为复杂,因此哪种方法更好其实没有一个定论,但在不同的情况下综合使用不同方法,一定能帮助读者提升命令行操作效率,而本文主要介绍的鼠标定位主要还是为了填补部分场景下的空缺。希望本文能对读者有所帮助、有所启发。