公司里有一批祖传脚本,代码写得又长又臭,领导让你重构。既然要重构,就难免要修改代码中的变量和函数,但是Shell语言目前并没有IDE之类的工具,没办法进行代码智能重构,所以我们在重构变量/函数的时候不能自动地把相关的引用代码一起修改掉,怎么办呢?

这时候就轮到编辑器之神Vim出场了,我们可以活用Vim的quickfix模式来修改所有与重构相关联的代码,而且只需三个步骤。

第一步是定位。Vim需要quickfix列表来确定哪些位置需要fix,这里有两个问题,第一个是我们如何找到需要修改的位置,第二个是如何输出符合格式的quickfix列表。对于前者,我们可以用grep命令来解决,对于后者,在grep的输出中添加位置信息即可,完整的命令为:grep --recursive --line-number 'regexp' /path/to/grep

第二步是传参。有了quickfix列表后,我们还需要用vim -q file命令来激活quickfix模式,看到这里,你可能会以为我们需要把第一步得到的grep输出存到一个文件里提供给Vim,但这里我们可以利用Bash的Process Substitution功能,直接把grep命令的输出当成文件传给Vim,命令的形式为vim -q <(grep 命令)

第三步是编辑。激活了quickfix模式后,就是我们用Vim大展身手的时刻了。quickfix模式下我们可以用 :cn[ext]/ :cp[revious]命令来跳转到下一个/上一个需要编辑的位置,然后逐个进行修改。但是,只是这样操作的话,哪里对得起Vim编辑器之神的名号?所以这次我们来一波“执行→重复→回退”的Vim套路:使用了quickfix命令后,上一次执行的命令会保存在:寄存器里,我们可以用@:命令调用:寄存器里的命令,然后再用@@命令重复执行上一次@{寄存器}命令。这样我们就能用@@命令快速遍历quickfix列表,而不用一遍遍地敲:cn[ext]/ :cp[revious]命令了。要是想再进阶一点的话,我们还能组合一下录制寄存器的功能,把两个quickfix命令分别录制到不同的有名寄存器里,然后@{寄存器}命令进行快速调用,这里就不展开细讲了。

总结一下上面的三个步骤,其实就是下面的一条命令和一张状态图。

vim -q(1) <((2) grep –recursive –line-number(3) ‘regexp’ /path/to/grep)

  1. -q 选项激活quickfix模式
  2. 用Bash的Process Substitution功能将grep的输出当成文件传给Vim
  3. –line-number选项调整grep输出格式
digraph G {
    rankdir=LR

    empty[label="寄存器 :|" shape=record]
    copied[label="寄存器 :| :cnext" shape=record]
    history[label="@@记录| :cnext" shape=record]
    empty -> copied [label="执行:cnext命令"]
    copied -> history [label="执行@:命令"]
    history -> history [label="执行@@命令"]
}

从上面的总结我们可以看到,Vim的强大之处在于它丰富的模式和命令,以及可以组合各种编辑操作的灵活性。这些优点让我们几乎总是能制定高效的Vim方案来满足我们的编辑需求。希望大家以后能多探索一下Vim中没使用过的模式和命令,并尝试一下组合这些操作,说不定以后你也能找到自己的Vim妙招呢。