从实践出发0基础入门vim,覆盖日常常用操作

使用Python简单实现一个Linux命令行工具: grep

引言

有这么一则笑话,如何得到一个随机字符串?答:让新手用户退出vim。vim不同于windows(大部分同学在windows上进行文本编辑)上的记事本(notepad),打开软件或者文件之后可以直接进行文本编辑。

当你打开vim时,你按下的每一个按键可能都不是你想要的效果。但是,一但你对这些按键的作用了如指掌并且形成了肌肉记忆,那文本编辑的体验与效率将直线起飞。vim不需要鼠标(当然你也可以使用)来移动光标,选中等等操作,这些全部都是键盘实现,这也让使用vim的人多一份hack感不是?🐶

或许有同学会问为什么要学习vim? 我现在编辑器用的很好,快捷键我也记住了很多,日常足够用了。没错,现在的IDE已经很完善了,各种功能,各种快捷键完全能让编辑体验飞起。从我自身来说,我接触使用过的文本编辑器和IDE还是挺多的,主流的例如vim,emacs,vscode,vs系列,sublime,JetBrains系列。在开发java或者kotlin等时我更倾向JetBrains全家桶,前端开发则会选择vscode,后端go,rust,python等使用vim,移动端开发可以使用AndroidStudio或者vscode(使用前端技术开发移动应用时),emacs用来写写小体量的代码或者使用org mode做笔记,agenda记录日程。

从我自身的例子来说,确实没有必要死磕一个编辑器或者IDE, 只要自己使用舒服即可。(当然我自己还是比较中意vim的,以上编辑器我全部使用了vim插件来模拟vim的操作)因此这篇文章只是我自己对vim日常使用的一些操作,当然无法覆盖完全,需要的话可以直接查看vim的官方文档或者相关书籍。

所谓编辑器之神,网络上的vim教程可谓不胜枚举,其中相当多一些确实回答了如何让新手退出vim这个问题(开个玩笑😁), 但是缺乏让人印象深刻的例子来提高新手对vim的体验。当然我自身的vim经历是比较枯燥的,是从单纯的记忆开始,中途也停止过一段时间的使用。

但是最终还是慢慢将所有的文本编辑都迁移到使用vim上来了。迁移过程本身其实是一个适应过程,相较于记忆还是体验不错的。由于vim的操作实在太多,而我常用操作就那些,很多不常用的在我的新手时期就开始记忆,因为没有得到实践就渐渐忘记,这是让我最难受的地方,也由此诞生了这篇文章的想法,就是从一个简单的实践中覆盖大部分我的日常vim使用操作,也希望能给他人一个从0开始学习vim的参考。

Linux命令行工具: grep

知之愈明,则行之愈笃; 行之愈笃,则知之益明。
——朱熹

既然要从实践出发,那就从实现一个命令行工具开始吧。我打算实现一个简单的grep命令来熟悉基本的vim操作。grep命令不知道大家熟悉不,就是打印存在给定匹配项的文本相应行。

开始之前为了好实验,还是准备一个文本吧,我就直接使用下面的命令得到Python之禅的文本咯。

python -c "import this" > this.txt

使用python解释器解释执行 import this 并将输出放到标准输出,最后通过重定向符 > 将文本重定向到名为 this.txt的文件(如果不存在就创建,如果存在就会将之前的文件清空,如果要追加则使用 >> )。 这样就准备好了文本。

Python之禅文本所在的文件名为this.txt。现在我想找到所有包含 implementation 单词的行,使用grep可以进行筛选。


The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

使用cat将文本内容打印到标准输出再使用管道符将cat命令的输出作为grep命令的输入。

cat this.txt | grep implementation

从结果中可以看到,grep将后面的implementation作为参数,将存在对应单词的行打印并且高亮对应的单词。

在了解grep的基本用法之后,那用python来实现一个我们自己的grep吧!

脱离鼠标

首先,使用vim,我想要的第一件事情就是脱离鼠标。先来打开一个py文件吧。在vim后空格跟一个文件名,如果文件存在就打开该文件,不存在就创建(当然没保存就退出是没有创建的)。

vim pgp.py

下面应该是大家看到的界面。

这个时候啥也不要按喔,因为此时vim处于normal模式下,你的每一个按键都有不同的含义。既然当下什么也没有那就先敲点什么吧。

字母i进入插入模式

本节涉及vim操作

vim操作按键

操作所在的模式

效果

i

normal

进入插入模式(可编辑文本)

Esc

insert

从插入模式回到normal模式

:q

command

退出vim

:w

command

保存文件

:wq

command

保存之后可以退出vim

:q!

command

不保存强制退出

要进入insert也就是插入模式的话有相当多的按键,这里就直接按键盘上的 字母 i 就能进入插入模式 啦,进入插入模式之后便可像普通记事本一样(但远远不止)进行编辑啦。想要退出vim的话可以按 Esc进入normal模式,在normal模式下输入 : 再按下 q 最后回车便可以退出。

在当然不止一个命令 q,还有 w 保存文件,! 强制执行命令,这些命令可以组合,比如要实现文件的强制保存并退出可以使用 :wq! 并回车。

先写一个获取命令行参数的函数吧。

normal模式下的光标移动基础

本节涉及vim操作

vim操作按键

操作所在的模式

效果

h

normal

向左移动一个字符位置的光标

j

normal

向下移动一个字符位置的光标

k

normal

向上移动一个字符位置的光标

l

normal

向右移动一个字符位置的光标

Esc

insert

从插入模式退出到normal模式

Ctrl+[

insert

从插入模式退出到normal模式

Ctrl+c

insert

从插入模式退出到normal模式

O(大写字母O)

normal

在光标上一行开始进入插入模式

gg

normal

光标跳转到文本起始

w

normal

向后移动一个word(单词)

W

normal

向后移动一个WORD(只有空白符分割的字符)

o(小写字母o)

normal

在光标下一行开始进入插入模式

M

normal

将光标移动到文本中央

Ctrl+w

insert

删除光标前面一个单词(注意,当光标是block也就是块状时,前面是指方块左边的线开始)

cc

normal

删除当前行但是光标停留在当前行

dd

normal

删除当前行,光标进入上一行

emmm, 好像sys这个模块忘记导入了,那我们就在文件最开头全局导入一下sys模块。但是可不要想去用鼠标,这一小节的内容就是脱离鼠标。当前我们的光标在第二行末尾,有相当多的方法将光标移动到行首。

在插入模式下我们可以使用方向键来移动光标,但是我是十分不推荐的,这样需要将手指离开键盘的主要区域。所以要移动光标我们需要 退回到normal模式,退回到插入模式就能移动啦。

而退回normal模式可以使用键盘上的 esc 退回normal模式也可以使用 Ctrl+[ 或者 Ctrl+c, 通过它们都能从插入模式进入normal模式。在退回normal模式之后便可以使用hjkl来移动光标。因为这里文本量并不大,使用hjkl来单个字符移动光标并没有什么不妥,但是一旦文本量上来了就不一样了。

现在先退回normal模式再将光标移动到d字母的位置。由于在normal模式下,我们还要进入insert模式添加一句 import sys。之前说了进入插入模式的按键很多,现在可以按大写字母O在光标的上一行进入插入模式。

每次想要移动光标的时候记得先进入normal模式喔。肯定有人会觉得这很麻烦,还不如用鼠标。其实我一开始也是这样想的,但是在习惯乃至对那些按键形成肌肉记忆之后,文本编辑体验绝对上了一个台阶。

假如我们光标停在如下图位置的画,想要走到文本开头还能使用 gg 回到文本开头。

到达文本开头之后再按i进入插入模式

简单写完这个函数之后,我现在想要跳转到文本末添加一个main代码块,可以是使用 G 直接跳转到文本末尾。再按 o 小写的o直接在下一行进入插入模式。

现在来稍微完善一下获取命令行参数的函数,这里只做简单的判断,即判断命令行参数的个数。注意看键盘操作。

这里直接使用大写 M 将光标放置到文本中央(行数的中心),由于出错,但是我又不想退出插入模式,因此在插入模式下直接使用 Ctrl+w 来删除光标前面的字符(这里我的显示有问题,但文本确实被删除了)。按下 : 进入command模式,输入字母 w 按回车保存文件。

OK,第一个函数写完了,也学了一些vim操作了,现在我们来运行一下,我现在希望是我在运行文件名之后跟一个字符串他能接收到。先来改进一点点。

这里看到使用 cc 来删除一行,实际上 c (change)以及类似的 d (delete)都可以看作是删除。而后面跟进一个vim中的所谓text object便可进行相关的操作。那什么是text object呢?

Text Object

本节涉及vim操作

vim操作按键

操作所在的模式

效果

y

normal

复制文本到寄存器(此处寄存器可以自己选择)

d

normal

配合text-object删除文本到寄存器(也可以看作是剪切)

c

normal

配合text-object删除文本到寄存器但是保留空白符号(也可以看作是剪切)

v

normal

visual模式下选中文本

p

normal

黏贴寄存器的文字到指定位置

0

normal

回到行首

A

normal

光标到达行尾并且进入插入模式

I

normal

光标到达行首并且进入插入模式

Ctrl+a

normal

数字递增1

Ctrl+x

normal

数字递减1

在vim中有text-object的概念。其可以指代一个单词,句子,段落等等。通过i,a等操作可以极大提高文本编辑效率。下面是一个例子。

有这么一段在小括号内的文本

if len(data.length) == 1:
  ...

我想要修改括号内的文本,可以将光标停留在 ( 或者 ) 或者小括号中的任意一个字符上,之后直接使用 di( 来删除括号内的文本。想要恢复之前的操作可以使用 u 来返上一次的操作。Ctrl+r 重现刚刚的操作。

这只是个例子,这样的操作还有很多,比如 da[ 连同 [] 包括括号内的文字一起删除,其他就让大家举一反三啦。

在vim中d,c,v,y都能类似进行操作。d用于删除,c即change,v在visual模式下选中,y即yank复制的意思。就比如下面的几个日常操作。

如最开始所说的几个基本操作,复制一行 yy,将文本黏贴在下一行 p,黏贴在上一行 P,以及光标进入行首或者行尾并且进入插入模式的转跳分别为 I 和 A,0 光标移动到行首但是不进入插入模式,这里补充一个 $ 光标移动到行尾但是不进入插入模式。v 用于选中文本,这里的 vw 就是选中一个word。当然也可以使用 V (大写的字母v) 来选中一行,之后可以进行其他操作。

其中最有意思的还是 sys.argv 的下标递增的实现,首先使用 yy 复制了这一行之后,因为我需要黏贴3行,所以直接先输入3再按p,这也是vim操作的一个特点,你可以在这些操作前添加数字来表示要运行后面的命令多少次。

为了选中数字1,我并没有使用 w 等来移动光标,而是使用了 f,这一个就放在下一个小节来说说吧。

再这之后,有意思的就来啦,使用 Ctrl+v 进行块选 选中数字1,移动光标选中下面的每一个1,最后按下g,再按 Ctrl+a 这样就实现了数字的递增。其实 Ctrl+a 和 Ctrl+x 分别是对数字的单次加1减1 通过g来实现递增。这个功能其实在写一些简单包含下标等操作的测试的时候我会常用到。by the way 顺便说一句,自己写的程序,大家要先多做测试喔。

还有更多的g相关的操作可直接参考vim的文档。

更多相关操作如果后续涉及到就说一下,这篇文章只是带大家入门vim,如果还需要更多知识的话,还是建议直接查看官方文档喔。在有一定基础上再去看文档会更加如鱼得水。

回到之前的程序,在尝试运行的时候发现了一个问题,直接报错了,应该是哪里写错了。直接来看看程序。


import sys


def get_sys_argv():
	if len(sys.argv) != 1:
		print("Command line argument error")
		exit(2)
	return sys.argv[1]

if __name__ == "__main__":
	get_sys_argv()

走到print说明参数长度不为1。确实,sys.argv 的第一个参数应该是程序本身,从第二个参数开始也就是从下标为1的参数开始才是我们跟在程序后面的命令行参数。那现在就把1改成2就行了。

这个时候又涉及到新的光标移动知识了,我们可以使用 hjkl 来移动单个字符,或者 w 来一个单词一个单词移动,但是还是太慢了,我们编辑文本虽然已经脱离了鼠标,但是目前效率还不够高。下面就来提升一下光标移动的效率。

提升编辑效率

修改之前的程序,把1改成2。大家应该都知道了,就是上一节的 f 和 Ctrl+a, 没错但也不止如此。先来看下面的操作。

这里先用 f 直接将光标移动到1,也演示了使用 t 先到达1字符之前一个字符,再按 l 向后移动一个字符到达1(当然这里只是为了演示 t 的使用) 然后,可以使用 Ctrl+a 将1增加到2,也可以使用 r (replace) 将1字符替换为2。除了先向下移动在向右移动,还能在normal模式下先输入 / 后输入要查找的字符串,回车之后直接跳转到当前行之后的第一个匹配项,要向当前行向上查找可以使用 ?。移动到下一个匹配项可以使用 n,移动到之前的匹配项 N。

除了行内向前查找,行内向后查找可以使用 F,如果想移动到下一个配项可以使用 ; (分号),移动到之前的匹配项可以使用 ,

行内的快速定位解决了,如果文件很长呢?翻页文件可以使用 Ctrl+f(向下翻页),Ctrl+u(向上翻页),分别将文本移动之顶端、中心、底部( zt, zz, zb)。这些都是在normal模式下使用的喔,因为这个程序很短这些就不演示了。

既然基础移动和查找都学习过了,那就先实现一个高亮指定文本的函数吧。

实现高亮文本函数

已经了解了很多操作了,就把基础的高亮对应文本实现吧。

这里高亮操作使用了中端下的ANSI控制码,这里33代表的是将字体设置为黄色,44表示的是将字背景设置为蓝色,更多颜色代码感兴趣的话,大家可以自己去查查,下面我放一个简单的字体以及背景对应颜色代码表,后面有想法的话可以给这个命令行工具添加修改颜色参数的功能顺便来练习vim

  • 颜色及其对应代码列表

字体颜色

颜色代码

背景颜色

颜色代码

重置

0

重置

0

黑色

30

黑色

40

红色

31

红色

41

绿色

32

绿色

42

黄色

33

黄色

43

蓝色

34

蓝色

44

洋红

35

洋红

45

青色

36

青色

46

白色

37

白色

47

还是先来测试一下这个函数吧。

实现grep基本功能

核心功能(高亮文本)已经实现。日常基本的操作已经介绍了,这里要说明对于 #[Text Object](Text Object) 那一块建议大家直接文档,应为有很多好玩的。虽然我们现在是脱离了鼠标,编辑文本也可以了,但是...这玩意就和记事本一样,太丑啦。即使有这么便捷的操作我自己也不愿意用哇。在实用的基础上,进行自定义还是很香的,(vim和neovim可是有相当多的插件哇) 所以后面一个函数 match 的实现我就直接使用自己日常的neovim(这是一个vim仓库的folk)来写。记录一下日常。

视频中会看到我返回normal模式使用的是连续的 jk。这个是可以自己配置的,vim是高度可定制的。

最简单的就是执行下面的语句,含义为向在主目录下的名为 .vimrc 的文件中输入 inoremap jk <Esc> (实际就是将键盘的 Esc 功能映射到连续按下 jk)

echo "inoremap jk <Esc>" > ~/.vimrc

再次启动vim之后,便可以使用 jk 来代替 Esc 来进入normal模式了。下面就把这几个函数组合起来。

ok,最后组合起来看看效果。

可以改变对应的颜色代码实现不同的输出效果。在整个体验上还差一点感觉,就是grep命令不需要这个python来运行哇,能不能像grep一样,在管道符号 | 之后直接跟上命令本身,之后再跟上需要查找的参数呢?

这个肯定是可以的啦。可以在文件头加上 #!/bin/python 来表明这是一个python脚本,使用python解释器来运行,最后再给文件加上可执行权限(以上操作在类Unix系统下,我自己当前是Linux系统)。

其实在vim中还有很多实用的操作比如宏、mark等,下面就是一个使用宏的例子。我现在需要将中文全部改为前面对应的英文单词,但是我想要单词开头字母大写其他小写

就是这么个需求看vim如何实现。

这里就是简单录制一个宏 normal模式下 q 开启录制宏,宏的名字为q,在操作完一行之后(这里说一下,~ 用于字母大小写转换),再次按 q 结束宏录制, 使用大写 V 选中一行,再按 G 选中到文本末尾,输入 :,会看到下面的命令输入框前面是 '<,'>,这个表示的是可是模式下选中的文本。后面跟上normal,之后使用 @q 来对选中的文本执行之前录制的名字为q的宏。

总结

我认为学习一项工具技术最实际的方法便是去上手操作。学习vim也是一样,可能一开始记忆很多的按键是很不容易,但是在实践中去学习,能让你的大脑和手指更快去适应(之后的编辑各种按键的功能,你是不需要去思考的,完全是肌肉记忆,编辑体验飞起)。入门之后,可以将vim带入日常的其他文本编辑,一旦形成了肌肉记忆(并且能盲打的话),文本编辑体验会大大提升。可能从那之后你便无法离开vim了。😄 加油吧!---来自一位Linux爱好者

附录:

pgrep.py


#!/bin/python
import sys

def hight_light_text(text: str, foreg=33, backg=44) -> str:
    '''返回高亮指定文本
    Args:
        text: 需要高亮的文本
        foreg: 字体颜色代码
        backg: 背景颜色代码
    '''
    return f"\033[1;{foreg}m\033[1;{backg}m" + text + "\033[0m"

def get_sys_arg() -> str:
    '''返回命令行参数
    '''
    if len(sys.argv) != 2:
        print("命令行参数错误")
        exit(2)
    return sys.argv[1]

def match(arg: str) -> None:
    '''在指定文件中匹配并高亮命令行输入的文本
    Args:
        arg: 需要匹配的文字
    '''
    for line in sys.stdin:
        if arg in line:
            line = line.strip()
            print(line[:line.index(arg)] + hight_light_text(arg) + line[line.index(arg) + len(arg):])
            


if __name__ == "__main__":
    match(get_sys_arg())
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值