Vim的问题是您不喜欢vi。
您提到用剪切,yy
并抱怨您几乎从不希望剪切整条线。实际上,在编辑源代码时,程序员经常希望处理整行,行范围和代码块。但是,yy
这只是将文本吸引到匿名复制缓冲区(或在vi中称为“注册” )的多种方法中的一种。
vi的“ Zen” 是您在说一种语言。初始y
是动词。该语句yy
是的同义词y_
。将y
是成倍上涨,使之更容易输入,因为它是这样一个常见的操作。
这也可以表示为dd
P
(删除当前行,然后将副本粘贴回原位;将副本留在匿名寄存器中作为副作用)。在y
与d
“动词”采取任何行动作为他们的“主题”。因此yW
是“从此处(光标)到当前/下一个(大)单词的末尾y'a
拉动”,并且是“从此处到包含名为' a ' 的标记的行。
如果您仅了解基本的上,下,左和右光标移动,那么vi不会比复制“记事本”更有效。(好吧,您仍然可以突出显示语法,并能够处理大于45KB左右的文件;不过请在这里与我合作)。
vi有26个“标记”和26个“寄存器”。使用m
命令将标记设置到任何光标位置。每个标记都由一个小写字母表示。因此,ma
将' a '标记设置为当前位置,并mz
设置' z '标记。您可以使用'
(单引号)命令移至包含标记的行。因此'a
移至包含' a '标记的行的开头。您可以使用`
(backquote)命令移至任何标记的精确位置。因此`z
将直接移动到“ z ”标记的确切位置。
因为这些是“动作”,所以它们也可以用作其他“陈述”的主题。
因此,剪切任意选择的文本的一种方法是删除标记(我通常使用' a '作为我的“第一个”标记,' z '作为我的下一个标记,' b '作为另一个标记,以及' e '作为还有另一种(我不记得在使用vi的 15年中曾经交互式使用过四个以上的标记;一个创建了自己的约定,关于不干扰一个人的交互上下文的宏如何使用标记和寄存器)。到我们想要的文本的另一端;我们可以从任一端开始都没关系。然后我们可以简单地使用d`a
剪切或y`a
复制。因此,整个过程有5次击键开销(如果以“插入”模式和需要Esc输出命令模式)。剪切或复制后,粘贴一个副本即可p
。
我说这是剪切或复制文本的一种方法。但是,它只是众多之一。通常,我们可以更简洁地描述文本的范围,而无需在光标周围移动和放置标记。例如,如果我在文本的段落中,则可以分别使用{
和}
移动到该段落的开头或结尾。因此,要移动一段文字,我使用{
d}
(3个按键)将其剪切了。(如果我碰巧已经在该段落的第一行或最后一行,则可以分别使用d}
或d{
。
“段落”的概念默认情况下通常是直观上合理的。因此,它既适用于代码,也适用于散文。
通常,我们知道某种模式(正则表达式)可以标记我们感兴趣的文本的一端或另一端。向前或向后搜索是vi中的动作。因此,它们也可以在我们的“陈述”中用作“主题”。因此,我可以d/foo
从当前行剪切到包含字符串“ foo”的下一行,并y?bar
从当前行复制到包含“ bar”的最新(上一个)行。如果我不希望整行,我仍然可以使用搜索动作(作为它们自己的语句),放下我的标记并使用`x
前面所述的命令。
除“动词”和“主语”外,vi还具有“宾语”(在语法意义上)。到目前为止,我仅描述了匿名寄存器的用法。但是,我可以通过在“对象”引用前面加上"
(双引号修饰符)来使用26个“命名”寄存器中的任何一个。因此,如果使用,"add
则将当前行剪切到' a '寄存器中;如果使用"by/foo
,则将文本的副本从此处拖到包含“ foo”的下一行到' b '寄存器中。要从寄存器中粘贴,我只需在粘贴之前添加相同的修饰符序列即可:"ap
粘贴“ a '寄存器” 的副本"bP
将副本从' b ' 粘贴到当前行之前。
“前缀”的概念还将语法“形容词”和“副词”的类似物添加到我们的文本操作“语言”中。大多数命令(动词)和运动(动词或对象,取决于上下文)也可以使用数字前缀。3J
表示“加入下三行”,d5}
表示“从当前行到第五段末尾从此处删除。”
这都是中级水平vi。它们都不是特定于Vim的,如果您准备学习它们,vi中还有许多高级技巧。如果您只掌握这些中间概念,那么您可能会发现几乎不需要编写任何宏,因为文本操作语言足够简洁明了,可以使用编辑器的“本机”语言轻松地完成大多数事情。
一些更高级技巧的样本:
有许多:
命令,最著名的是:% s/foo/bar/g
全局替换技术。(这不是高级功能,但其他:
命令也可以)。整个:
命令集在历史上一直由vi的前身作为ed(行编辑器)和后来的ex(扩展行编辑器)实用程序继承。实际上vi之所以如此命名是因为ex是ex的可视界面。
:
命令通常在文本行上运行。 ed和ex是在终端屏幕不常见且许多终端是“电传”(TTY)设备的时代编写的。因此,通常是通过非常简洁的界面使用命令来处理文本的印刷副本(常见的连接速度为110波特,或大约每秒11个字符-这比快速的打字员要慢;通常会出现滞后)多用户互动会话;此外,通常还有一些节省纸张的动机。
因此,大多数:
命令的语法包括地址或地址范围(行号),后跟命令。自然地,可以使用文字行号::127,215 s/foo/bar
将127和215之间的每一行的“ foo”的首次出现更改为“ bar”。还可以使用一些缩写,例如.
或$
分别用于当前行和最后一行。还可以使用相对前缀+
,-
分别引用当前行之后或之前的偏移量。因此::.,$j
意思是“从当前行到最后一行,将它们全部合并为一行”。 :%
是:1,$
(所有行)的同义词。
在:... g
和:... v
他们是非常强大的命令承担一些解释。 :... g
是“全局”前缀,将“后续”命令应用于与模式(正则表达式)匹配的所有行,而:... v
将此类命令应用于与给定模式不匹配的所有行(“ conVerse”中的“ v”)。与其他ex命令一样,这些命令可以通过寻址/范围引用作为前缀。因此,:.,+21g/foo/d
意味着“从当前行中删除包含字符串“ foo”的任何行到下一行21行”,而:.,$v/bar/d
意味着“从此处到文件末尾,删除不包含字符串” bar”的任何行。
有趣的是,常见的Unix命令grep实际上是受此ex命令启发的(并以其记录方式命名)。该前命令:g/re/p
(grep的)是他们记载如何“全局”,“打印正则表达式”(RE)“含有一个行”的方式。使用ed和ex时,该:p
命令是任何人都学到的第一个命令,并且通常是编辑任何文件时使用的第一个命令。这就是您打印当前内容的方式(通常每次使用一页:.,+25p
或多页仅打印一页)。
请注意,:% g/.../d
或(其reVerse / conVerse对应项::% v/.../d
是最常见的用法模式。但是,还有一些其他ex
命令值得记住:
我们可以使用m
来移动线和j
连接线。例如,如果您有一个列表,并且想要将所有匹配的东西(或者相反不匹配某种模式)分离而不删除它们,则可以使用类似以下内容::% g/foo/m$
...并且所有“ foo”行都将移至文件末尾。(请注意有关将文件结尾用作暂存空间的另一条提示)。从列表的其余部分中提取它们时,这将保留所有“ foo”行的相对顺序。(这等效于执行以下操作1G!GGmap!Ggrep foo<ENTER>1G:1,'a g/foo'/d
:(将文件复制到自己的尾部,通过过滤尾部grep
,并删除头部的所有内容)。
通常,要连接行,我可以找到所有需要连接到其前身的行的模式(例如,在某些项目符号列表中,所有以“ ^”而不是“ ^ *”开头的行)。对于这种情况,我将使用:(:% g/^ /-1j
对于每条匹配的行,请向上一行并加入它们)。(顺便说一句:对于子弹名单试图寻找子弹线,并加入到下一个确实的一对夫妇理由不工作...它可以加入一个子弹一行到另一个,而且不会加入任何子弹线,所有的它的延续;只能在比赛中成对使用)。
几乎不用说,您可以使用我们的老朋友s
(替代)和g
and v
(全局/反向全局)命令。通常您不需要这样做。但是,考虑某些情况,您只想在与某些其他模式匹配的行上执行替换。通常,您可以对捕获使用复杂的模式,并使用向后引用保留不想更改的行部分。但是,将匹配与替换分开通常会更容易::% g/foo/s/bar/zzz/g
-对于包含“ foo”的每一行,将所有“ bar”替换为“ zzz”。(就像是:% s/\(.*foo.*\)bar\(.*\)/\1zzz\2/g
仅适用于同一行上以“ foo”开头的“ bar”实例的情况;它已经足够笨拙了,必须进行进一步的处理才能捕获“ bar”在“ foo”之前的所有情况)
问题的关键是,有不仅仅是p
,s
以及d
在线路ex
命令集。
这些:
地址也可以引用标记。因此,可以使用::'a,'bg/foo/j
将包含字符串foo的任何行连接到其后一行(如果它位于' a '和' b '标记之间的行之间)。(是的,所有前面的ex
命令示例都可以通过添加这些寻址表达式的前缀而被限制为文件行的子集)。
这非常晦涩(在过去的15年中,我仅使用过几次类似的东西)。但是,我会自由地承认,我经常迭代地进行交互操作,如果我花时间考虑正确的咒语,可能会更有效率。
另一个非常有用的vi或ex命令是:r
读入另一个文件的内容。因此::r foo
在当前行插入名为“ foo”的文件的内容。
:r!
命令更强大。这将读取命令的结果。这与暂停vi会话,运行命令,将其输出重定向到临时文件,恢复vi会话以及从temp读取内容相同。文件。
更强大的是!
(bang)和:... !
(ex bang)命令。它们还执行外部命令,并将结果读入当前文本。但是,它们还会通过命令过滤对文本的选择!这样,我们可以使用1G!Gsort
(G
是vi “ goto”命令;对文件中的所有行进行排序;默认为转到文件的最后一行,但可以在行号之前加上前缀,例如第一行)。这等效于ex变体:1,$!sort
。编写者通常!
与Unix fmt或fold实用程序一起使用,以重新选择或“自动换行”文本选择。一个非常常见的宏是{!}fmt
(重新格式化当前段落)。程序员有时会通过缩进或其他代码重新格式化工具使用它来运行其代码,或仅运行其一部分。
使用:r!
和!
命令意味着任何外部实用程序或过滤器都可以视为我们编辑器的扩展。我偶尔将它们与用于从数据库中提取数据的脚本,用于从网站中提取数据的wget或lynx命令或用于从远程系统中提取数据的ssh命令一起使用。
另一个有用的ex命令是:so
(的缩写:source
)。这将文件的内容作为一系列命令读取。当您正常启动vi时,它会隐式地执行一个:source
on ~/.exinitrc
文件(而Vim通常会~/.vimrc
自然地在on上执行此操作)。这样做的用途是,您只需输入一组新的宏,缩写和编辑器设置,即可即时更改编辑器配置文件。如果您偷偷摸摸,甚至可以使用它作为技巧来存储按顺序应用于文件的ex编辑命令序列。
例如,我有一个七行文件(36个字符),该文件通过wc运行文件,并在包含该字数数据的文件顶部插入C样式的注释。我可以使用以下命令将“宏”应用于文件:vim +'so mymacro.ex' ./mytarget
(vi和Vim的+
命令行选项通常用于在给定的行号处启动编辑会话。但是,鲜为人知的事实是,任何有效的ex命令/表达式都可以跟随它,例如“ source”命令,例如我已经在这里完成;作为一个简单的示例,我有一些脚本调用:在重新映像一组服务器时,以非交互方式从我的SSH已知主机文件中删除条目)。+
vi +'/foo/d|wq!' ~/.ssh/known_hosts
通常,使用Perl,AWK,sed编写这样的“宏”要容易得多(实际上,这与grep一样,是受ed命令启发的实用程序)。
该@
命令可能是最模糊的vi命令。在将近十年的时间里不时地讲授高级系统管理课程时,我见过的很少有人使用过它。 @
就像执行vi或ex命令一样执行寄存器的内容。
示例:我经常使用::r!locate ...
在系统上查找某些文件并将其名称读入文档中。从那里我删除所有无关的内容,只保留我感兴趣的文件的完整路径。而不是费力地Tab遍历路径的每个组件(或者更糟糕的是,如果我碰巧被卡在没有Tab补全支持的机器上在vi的副本中,我只使用:
0i:r
(将当前行转换为有效的:r命令),
"cdd
(将行删除到“ c”寄存器中),然后
@c
执行该命令。
只需10次击键(该表达式"cdd
@c
对我来说实际上是一个手指宏,因此我几乎可以像输入任何常见的六个字母的单词一样快地键入它)。
一个发人深省的想法
我只接触过vi的功能,在这里我所描述的甚至都不是vim所命名的“改进”的一部分!我在这里描述的所有内容都可以在20或30年前的vi的任何旧版本上使用。
有些人使用了vi的力量比我用过的要多得多。