Linux运维——Vim技巧一
Vim技巧
- 一、优化重复操作
- 1.1、 . 命令
- 1.2、* 命令
- 1.3、重复修改示例
- 二、删除单词(daw)
- 三、对数字做算数运算
- 四、操作符与动作
- 五、插入模式
- 5.1、插入模式下删除
- 5.2、返回普通模式
- 5.3、插入-普通模式
- 5.4、不离开插入模式,粘贴寄存器中的文本
- 5.4、表达式寄存器
- 5.5、用字符编码插入字符
- 六、替换模式
- 7、可视模式
- 7.1、选择高亮区域(三种子模式)
- 7.2、重复执行面向行的可视命令
- 7.3、面向块的可视模式编辑
- 7.4、在长短不一的高亮块后添加文本
- 8、命令行模式
- 8.1、选定操作范围
- 8.2、复制和移动(:t和:m)
- 8.3、在指定范围上执行普通模式命令
- 8.4、重复上次的Ex命令
- 8.5、自动补全Ex命令
- 8.6、把当前单词插入到命令行
- 8.7、回溯历史命令
- 8.8、运行Shell命令
一、优化重复操作
1.1、 . 命令
.
命令可以让我们重复上次的修改。“上次修改”可以指很多东西,一次修改的单位可以是字符、整行,甚至是整个文件。
从进入插入模式的那一刻起(例如,输入 i
) ,直到返回普通模式时为止 (输入<Esc>
) , Vim 会记录每一个按键操作。 做出这样一个修改后再用 .
命令的话,它将会重新执行所有这些按键操作。
# 文档a.txt内容如下Line one
Line two
Line three
Line four# >G 命令会增加从当前行到文档末尾处的缩进层级。
# 如果我们在此命令后使用 . 命令,那么“重复上次修改”会让 Vim 增加从当前行到文档末尾的缩进层级。# 命令如下
>G j. j. j.# 最后文档如下Line oneLine twoLine threeLine four
1.2、* 命令
*
命令可以查找当前光标下的单词,等同于/xxxx
。
Line one
Line two
Line three
Line four
把光标移到单词“Line”上,然后使用 *
命令对它进行查找,这会产生两个结果:
- 一是光标会跳到下一个匹配项上
- 二是所有出现这个词的地方都会被高亮显示出来
如果你并没有看到高亮,试着运行一下 :set hls
。执行过一次查找“Line”的命令后,现在我们只需按 n 键就可以跳到下一个匹配项。
1.3、重复修改示例
# a.txt内容如下Line one
Line two
Line three
Line four# 将所有Line替换为XXX
# 1. 光标放到第一个Line上,*
# 2. cw 删除Line,输入 XXX,<ESC>
# 3. 按n跳转到下一个匹配的Line,按.重复修改
# 即 * cw XXX <ESC> n. n. n.
二、删除单词(daw)
# 光标位于行尾处的字符“h”上,而我们想要删除单词“nigh”
The end is nigh
反向删除:
按 db
命令删除从光标起始位置到单词开头的内容,但会原封未动地留下最后一个字符 “h” ,再按一下 x
键就可以删除这个捣乱的字符。
正向删除:
先用 b
命令把光标移到单词的开头,移动好后,就可以用一个 dw
命令删掉整个单词。
删除整个单词:
可以使用更为精准的 aw
文本对象 (text object) , 而不是用动作命令。
使用daw
一次删除整个单词,可以把 daw 命令解读为“delete a word” ,这样比较容易记忆。
三、对数字做算数运算
<C-a>
和 <C-x>
命令分别对数字执行加和减操作。在不带次数执行时,它们会逐个加减,但如果带一个次数前缀,那么就可以用它们加减任意整数。
如果光标不在数字上
<C-a>
命令会“把当前光标之上或之后的数值加上 [count]”。因此,如果光标不在数字上,那么<C-a>
命令将在当前行正向查找一个数字,如果找到了,它就径直跳到那里。我
们可以利用这一点简化操作。
# 假设光标位于首字符
This is 2 times;
This is 3 times;# 按下 10<C-a> 后
This is 12 times;
This is 3 times;
四、操作符与动作
操作符 + 动作命令 = 操作
d{motion}
命令可以对一个字符(dl
)、一个完整单词(daw
)或一整个段落(dap
)进行操作,它作用的范围由动作命令决定。c{motion}
、y{motion}
以及其他一些命令也类似,它们被统称为操作符(operator)。你可以用:h operator
来查阅完整的列表,下表总结了一些比较常见的操作符。
操作符 | 动作 |
---|---|
c | 修改 |
d | 删除 |
y | 复制到寄存器 |
g~ | 反转大小写 |
gu | 转换为小写 |
gU | 转换为大写 |
> | 增加缩进 |
< | 减小缩进 |
= | 自动缩进 |
! | 使用外部程序过滤{motion}所跨越的行 |
操作符与动作命令的结合形成了一种语法。这种语法的第一条规则很简单,即一个操作由一个操作符,后面跟一个动作命令组成。假如我们已经知道如何用 daw
删除一个单词,然后又学到 gU
命令。它也是个操作符,所以我们可以用 gUaw
把当前单词转换成大写形式。如果我们的词汇进一步扩充,学会了作用于段落的 ap
动作命令,就会发现我们可以进行两个新的操作:用 dap
删除整个段落,或者用 gUap
把整段文字转换为大写。
Vim 的语法只有一条额外规则,即当一个操作符命令被连续调用两次时,它会作用于当前行。所以 dd
删除当前行,而 >>
缩进当前行。gU
命令是一种特殊情况,我们既可以用 gUgU
,也可以用简化版的 gUU
来使它作用于当前行。
五、插入模式
5.1、插入模式下删除
在插入模式下,除了退格键可以删除。另外,我们还可以用下面这些组合键:
按键操作 | 用途 |
---|---|
<C-h> | 删除前一个字符(同退格键) |
<C-w> | 删除前一个单词 |
<C-u> | 删至行首 |
5.2、返回普通模式
在插入模式下,除了<Esc>
键可以返回普通模式。另外,我们还可以用下面这些组合键:
按键操作 | 用途 |
---|---|
<Esc> | 切换到普通模式 |
<C-[> | 切换到普通模式 |
<C-o> | 切换到插入-普通模式 |
5.3、插入-普通模式
插入-普通模式是普通模式的一个特例,它能让我们执行一次普遍模式命令。在此模式中,我们可以执行一个普通模式命令,执行完后,马上就又返回到插入模式。要从插入模式切换到插入-普通模式,可以按 <C-o>
。
在当前行正好处于窗口顶部或底部时,有时我会滚动一下屏幕,以便看到更多的上下文。用 zz
命令可以重绘屏幕,并把当前行显示在窗口正中,这样就能够阅读当前行之上及之下的半屏内容。我常常会键入 <C-o>zz
,在插入-普通模式中触发这条命令。此操作完成后就会直接回到插入模式,因此我可以不受中断地继续打字。
5.4、不离开插入模式,粘贴寄存器中的文本
我们想把本书的书名插到最后一行,以补全该行。由于书名在第一行的开头已经出现过了,我们将把它复制到一个寄存器中,然后在插入模式中把它添加到第二行结尾。
按键操作 | 缓冲区内容 |
---|---|
yt, | P ractical Vim, by Drew NeilRead Drew Neil’s |
jA空格 | Practical Vim, by Drew Neil Read Drew Neil’s |
<C-r>0 | Practical Vim, by Drew Neil Read Drew Neil’s Practical Vim`` |
.<Esc> | Practical Vim, by Drew Neil Read Drew Neil’s Practical Vim . |
yt,
命令把“Practical Vim”复制到复制专用寄存器中,然后我们在插入模式中,按 <C-r>0
把刚才复制的文本粘贴到光
标所在位置。
对面向字符的寄存器使用
<C-r>{register}
命令,其中{register}是我们想要插入的寄存器的名字。
5.4、表达式寄存器
大部分的 Vim 寄存器中保存的都是文本,要么是一个字符串,要么是若干行的文本。删除及复制命令允许我们把文本保存到寄存器中,而粘贴命令则允许我们把寄存器中的内容插入到文档里。
不过表达式寄存器则是个另类,它可以用来执行一段 Vim 脚本,并返回其结果。可以用 =
符号指明使用表达式寄存器。在插入模式中,输入 <C-r>=
就可以访问这一寄存器。这条命令会在屏幕的下方显示一个提示符,我们可以在其后输入
要执行的表达式。输入表达式后敲一下 <CR>
, Vim 就会把执行的结果插入到文档的当前位置了。
假设我们刚输入完下列内容:
6 chairs, each costing $35, totals $
我们想算一下总价,不过没必要找个信封在背面做演算,Vim 可以帮我们做这件事,我们甚至连插入模式都不用退出。
按键操作 | 缓冲区内容 |
---|---|
A | 6 chairs, each costing $35, totals $ |
<C-r>= 6*35<CR> | 6 chairs, each costing $35, totals $210 |
5.5、用字符编码插入字符
Vim 可以用字符编码(Character Code)插入任意字符。使用此功能可以很方便地输入键盘上找不到的符号。
只要知道某个字符的编码,就可以让 Vim 插入该字符,我们可以用这种方式插入任意字符。要根据字符编码插入字符,只需在插入模式中输入 <C-v>{code}
即可,其中{code}是要插入字符的编码。
Vim 所接受的字符编码共包含 3 位数字。例如,假设我们想插入大写字母“A”,它的字符编码是 65,因此我们需要输入 <C-v>065
。
然而,如果我们想插入一个编码超过 3 位数的字符该怎么办?比方说,Unicode基本多文种平面 (Unicode Basic Multilingual Plane) 的地址空间最大会有 65535 个字符。解决方法是可以用 4 位十六进制编码来输入这些字符,即输入<C-v>u{1234}
(注意数字前的 u ) 。假设我们想插入字符编码为 00bf
的反转问号( “¿
” ) ,只需在插入模式中输入 <C-v>u00bf
即可。
如果你想知道文档中任意字符的编码,只需把光标移到它上面并按 ga
命令,然后屏幕下方就会显示出一条消息,分别以十进制和十六进制的形式显示出其字符编码。
另外,如果 <C-v>
命令后面跟一个非数字键,它会插入这个按键本身所代表的字符。例如,如果启用了‘expandtab’ 选项,那么按 <Tab>
键将会插入空格而不是制表符。然而,按 <C-v><Tab>
则会一直插入制表符,不管‘expandtab’ 选项激活与否。
按键操作 | 用途 |
---|---|
ga | 查看编码 |
<C-v>{123} | 以十进制字符编码插入字符 |
<C-v>u{1234} | 以十六进制字符编码插入字符 |
<C-v>{nondigit} | 按原义插入非数字字符 |
<C-k>{char1}{char2} | 插入以二合字母{char1}{char2}表示的字符 |
六、替换模式
在替换模式中输入会替换文档中的已有文本,除此之外,该模式与插入模式完全相同。
命令 | 用途 |
---|---|
R | 普通模式进入替换模式 |
r{char} /gr{char} | 单次版本的替换模式及虚拟替换模式,允许我们覆盖一个字符,之后马上又回到普通模式 |
假设有如下一段文本:
Typing in Insert mode extends the line. But in Replace mode
the line length doesn't change.
我们想把这两个单独的句子合并成一句话,为此,需要把句号改成逗号,并将单词“But”中的“B”改为小写。
按键操作 | 缓冲区内容 |
---|---|
{start} | T yping in Insert mode extends the line. But in Replace mode the line length doesn’t change. |
f. | Typing in Insert mode extends the line. But in Replace mode the line length doesn’t change. |
R,空格b<Esc> | Typing in Insert mode extends the line. b ut in Replace mode the line length doesn’t change. |
7、可视模式
可视模式允许我们选中一个文本区域并在其上操作。我们仍可以把 h、j、k及 l当成光标键使用;也可以用 f{char} 跳到当前行的某个字符上,然后用 ; 和 , 命令相应地正向或反向重复此跳转;甚至还可以用查找命令(以及 n / N 命令)跳转到匹配指定模式的地方。每次在可视模式中移动光标,都会改变高亮选区的边界。
7.1、选择高亮区域(三种子模式)
激活可视模式:
命令 | 用途 |
---|---|
v | 激活面向字符的可视模式 |
V | 激活面向行的可视模式 |
<C-v> | 激活面向列块的可视模式 |
gv | 重选上次的高亮选区 |
可视模式间切换:
按键操作 | 用途 |
---|---|
<Esc> / <C-[> | 回到普通模式 |
v /V /C-v | 切换到普通模式(在对应的面向字符可视模式、面向行的可视模式或面向列块的可视模式中使用时) |
v | 切换到面向字符的可视模式 |
V | 切换到面向行的可视模式 |
<C-v> | 切换到面向列块的可视模式 |
o | 切换高亮区的活动端 |
切换选区的活动端:
高亮选区的范围由其两个端点界定。其中一端固定,而另一端可以随光标自由移动,我们可以用 o
键来切换其活动的端点。在定义选区时,如果定义到一半时,才发现选区开始的位置不对,此时用这个键会很方便,我们用不着退出可视模式再
从头开始,只需按一下 o,然后重新调整选区的边界即可。下面的操作对此功能进行了演示:
按键操作 | 缓冲区内容 |
---|---|
{start} | Select from here to h ere. |
vbb | Select from h ere to here. |
o | Select from here to h ere. |
e | Select from here to here . |
7.2、重复执行面向行的可视命令
假设有一段 Python 代码的缩进有些问题,如下所示:
def fib(n):a, b = 0, 1while a < n:
print a,
a, b = b, a+b
fib(42)
这段代码的每级缩进使用 4 个空格,首先我们得对 Vim 进行配置,使之符合此缩要想让 < 和 > 命令正常工作,我们需要把 ‘shiftwidth’ 及 ‘softtabstop’进风格。
:set shiftwidth=4 softtabstop=4 expandtab
# {start} def fib(n):a, b = 0, 1while a < n:
print a, # 光标位于p
a, b = b, a+b
fib(42)# Vj
def fib(n):a, b = 0, 1while a < n:
print a, # 选中此行
a, b = b, a+b # 选中此行
fib(42)# >. 重复缩进
def fib(n):a, b = 0, 1while a < n:print a,a, b = b, a+b
fib(42)
7.3、面向块的可视模式编辑
假设有如下一个纯文本表格:
Chapter Page
Normal mode 15
Insert mode 31
Visual mode 44
我们想用管道符画一条竖线来隔开这两列文本,使之看起来更像一个表格。但是在此之前,要先减少两列之间的间隔,使它们不要分得这么开。用面向列块的可视模式可以完成这两处修改,
{start} # 光标放在Chapter和Page中间
<C-v>3j # 向下选3行
x... # 删除并重复3次
gv # 再次选区高亮区域
r| # 替换模式下替换为|
yyp # 光标在Chapter行,复制并粘贴该行
Vr- # 切换到行可视模式,替换为-# 结果为
Chapter | Page
--------------------
Normal mode | 15
Insert mode | 31
Visual mode | 44
7.4、在长短不一的高亮块后添加文本
var foo = 1
var bar = 'a'
var foobar = foo + bar
这段代码有连续 3 行,每行的长度各不相同,而我们想在每行结尾添加一个分号。
{start} # 开始,光标在首行1位置上
<C-v>jj$ #开启块可视模式,选中到行尾
A; # 尾部添加;
<Esc> # 退出
确定好选区后,用 A 命令就可以在每行的结尾添加内容。此命令让我们进入插入模式,且使光标停留在顶行。处于插入模式期间,任何输入的内容只出现在顶行,然而一旦返回到普通模式,这些修改就会被扩散到其余选中的行上。
8、命令行模式
命令行模式会提示我们输入一条 Ex 命令、一个查找模式,或一个表达式。在按下 :
键时,Vim 会切换到命令行模式。这个模式和 shell 下的命令行有些类似, 我们可以输入一条命令, 然后按 <CR>
执行它。 在任意时刻, 我们都可以按 <Esc>
键从命令行模式切换回普通模式。
操作缓冲区文本的Ex命令:
命令 | 用途 |
---|---|
:[range]delete [x] | 删除指定范围内的行[到寄存器 x中] |
:[range]yank [x] | 复制指定范围的行[到寄存器 x中] |
:[line]put [x] | 在指定行后粘贴寄存器 x中的内容 |
:[range]copy {address} | 把指定范围内的行拷贝到{address} 所指定的行之下 |
:[range]move {address} | 把指定范围内的行移动到 {address} 所指定的行之下 |
:[range]join | 连接指定范围内的行 |
:[range]normal {commands} | 对指定范围内的每一行执行普通模式命令 {commands} |
:[range]substitute/{pattern}/{string}/[flags] | 把指定范围内出现{pattern}的地方替换为{string} |
:[range]global/{pattern}/[cmd] | 对指定范围内匹配{{pattern}的所有行,在其上执行Ex 命令{cmd} |
8.1、选定操作范围
很多 Ex 命令可以用 [range]
指定要操作的范围。我们可以用行号、位置标记或是查找模式来指定范围的开始位置及结束位置。
<!DOCTYPE html>
<html>
<head><title>Practical Vim</title></head>
<body><h1>Practical Vim</h1></body>
</html>
我们将使用:print
命令作为演示。 这条命令只是简单地在 Vim 命令行下方回显指定行的内容,它不产生什么实际影响,不过可以用它来说明一个范围由哪些行构成。当然,你可以试着把以下示例中的:print
换成诸如:delete
、:join
、:substitute
或:normal
这样的命令,这样就能真切地感受到 Ex 命令是多么有用。
用行号作为地址:
# 如果输入一条只包含数字的 Ex 命令,那么 Vim 会把这个数字解析成一个地址,并把光标移动到该数字所指定的行上。
:1
:p
# 或
:1p#输出
1 <!DOCTYPE html>
用地址指定一个范围:
# 语法 :{start},{end}
# 打印2到5行
:2,5p# 输出2 <html>3 <head><title>Practical Vim</title></head>4 <body><h1>Practical Vim</h1></body>5 </html># 当前行到最后一行
.,$p# 全文
%p
# 等价于 1,$p
用高亮选区指定范围:
我们也可以用高亮选区选定一个范围,而不是用数字指定。如果我们先执行 2G
,再跟着执行 VG
,就会选中如下一个高亮选区:
如果现在按下 :
键,命令行上就会预先填充一个范围 :'<,'>
。这个范围看起来有点晦涩难懂,不过你可以简单地把它理解为一个代表高亮选区的范围。接下来我们就可以输入一条 Ex 命令,使它在每个被选中的行上执行:
:'<,'>p
用模式指定范围:
Vim 也接受以模式作为一条 Ex 命令的地址,如下所示:
:/<html>/,/<\/html>/p# 输出2 <html>3 <head><title>Practical Vim</title></head>4 <body><h1>Practical Vim</h1></body>5 </html>
这个范围看起来比较复杂,但实际上它符合范围的一般形式:{start},{end}
。在本例中, {start}
地址是模式 /<html>/
,而 {end}
地址是 /<\/html>/
。换句话说,这个范围由 <html>
开标签所在的行开始,到对应闭标签所在的行结束。
用偏移对地址进行修正:
假设我们想对位于<html></html>
之间的每一行都运行一条 Ex 命令,但是不想包括 <html>
及 </html>
标签所在的行,那么可以为之加上偏移:
# 语法 :{address}+n
:/<html>/+1,/<\/html>/-1p#输出3 <head><title>Practical Vim</title></head>4 <body><h1>Practical Vim</h1></body>
总结:
符号 | 地址 |
---|---|
1 | 文件的第一行 |
$ | 文件的最后一行 |
0 | 虚拟行,位于文件第一行上方 |
. | 光标所在行 |
'm | 包含位置标记 m的行 |
'< | 高亮选区的起始行 |
'> | 高亮选区的结束行 |
% | 整个文件(:1,$ 的简写形式) |
8.2、复制和移动(:t和:m)
:copy
命令(及其简写形式:t
)让我们可以把一行或多行从文档的一部分复制到另一部分:move
命令(及其简写形式:m
)则可以让我们把一行或多行移到文档的其他地方
Shopping listHardware StoreBuy new hammerBeauty ParlorBuy nail polish removerBuy nails
用:t
命令复制行:
# 语法
:[range]copy {address}
这个购物清单还没完成,我们也要在五金商店(hardware store)买些钉子(nails) 。为完成这个清单,我们将重用文件的最后一行,即在“Hardware Store”下面为之创建一份副本。
{start} #开始,光标位于第二行H位置
:6t. # 复制第6行,并粘贴到当前行下# 结果如下1 Shopping list2 Hardware Store3 Buy nails4 Buy new hammer5 Beauty Parlor6 Buy nail polish remover7 Buy nails
应用实例:
命令 | 用途 |
---|---|
:6t. | 把第 6行复制到当前行下方 |
:t6 | 把当前行复制到第 6行下方 |
:t. | 为当前行创建一个副本(类似于普通模式下的 yyp) |
:t$ | 把当前行复制到文本结尾 |
:'<,'>t0 | 把高亮选中的行复制到文件开头 |
用:m
命令移动行:
# 语法
:[range]move {address}
假设我们想把 Hardware Store 一节移到 Beauty Parlor 一节的下方,用 :move 就可以实现这一点:
{start} # 开始,光标位于第2行H位置
Vjj # 进入列视图模式,并向下选中2行
:'<,'>m$ # 将选中高亮区域移动到最后一行后面# 结果1 Shopping list2 Beauty Parlor3 Buy nail polish remover4 Buy nails5 Hardware Store6 Buy nails7 Buy new hammer
8.3、在指定范围上执行普通模式命令
如果想在一系列连续行上执行一条普通模式命令,我们可以用 :normal
命令。此命令在与.
命令或宏结合使用时,我们只需花费很少的努力就能完成大量重复性任务。
们将在下面文件的每行后都添加一个分号,以此作为演示。
var foo = 1
var bar = 'a'
var baz = 'z'
var foobar = foo + bar
var foobarbaz = foo + bar + baz
{start} # 光标位于第一行v位置
A;<Esc> # 移动行尾进入插入模式输入;然后返回普通模式
jVG # 移动到下一行,进入行视图模式,选中到文档尾部
:'<,'>normal . #进入命令模式,normal命令执行.# 结果1 var foo = 1;2 var bar = 'a';3 var baz = 'z';4 var foobar = foo + bar;5 var foobarbaz = foo + bar + baz;
命令模式中,可以使用如下命令完成相同操作:
:%normal A;
8.4、重复上次的Ex命令
.
命令可以重复上次的普通模式命令。然而,如果想重复上次的 Ex 命令的话,我们得使用 @:
才行。
例如,下面两条命令在遍历缓冲区列表的条目时非常有用,用 :bn[ext]
可以在列表中逐项正向移动,而 :bp[revious]
命令则进行反向移动 。 假设缓冲区列表中有大约十几个条目, 而我们打算逐个查看每个缓冲区,因此可以输入一次下面的命令:
:bnext
然后再用 重复执行此命令。
8.5、自动补全Ex命令
如同在 shell 中一样,在命令行上也可以用 <Tab>
键自动补全命令。
Vim 在选取 Tab 补全的补全项时非常智能,它会检查命令行上已经输入的上下文,然后再构建合适的补全列表。例如,可以这样输入:
:col<C-d>
colder colorscheme
<C-d>
命令会让 Vim 显示可用的补全列表。另外,如果
我们多次按 <Tab>
键的话,命令行上会依次显示 colder、colorscheme,然后再回到最初的 col,如此循环往复。要想反向遍历补全列表,可以按 <S-Tab>
。
8.6、把当前单词插入到命令行
即使是在命令行模式下, Vim 也始终知道光标位于何处以及哪个分割窗口处于活动状态。为节省时间,我们可以把活动窗口中的当前单词(或字串)插入到命令行中。
在 Vim 的命令行下, <C-r><C-w>
映射项会复制光标下的单词并把它插入到命令行中。我们可以利用这一功能减少击键的次数。
假设我们想把下面这段代码中的变量 tally重命名为 counter:
var tally;
for (tally=1; tally <= 10; tally++) {// do something with tally
};
把光标移到单词 tally上后,用 *
命令就可以查找它出现的每处地方(* 命令等效于输入/\<<C-r><C-w>\><CR>
序列。
{start} # 开始光标位于第一行t位置
* # 查找出所有tally
cwcounter<Esc> #cw命令删除tally并进入插入模式tally,然后返回普通模式
:%s//<C-r><C-w>/g # 然 后 , 我 们 将 用 :substitute 命 令 完 成 其 余 的 修 改 。 由 于 光 标 已 经 在 单 词“counter”上了,因此我们无需再次输入它,而是直接用 <C-r><C-w>映射项把它插入到替换域
8.7、回溯历史命令
Vim 会记录命令行模式中执行过的命令,并提供了两种方式回溯这些命令,用光标键回滚之前的命令或调出命令行窗口查看先前的命令。
先按 : 键切换到命令行模式,在保持提示符为空的情况下按 <Up>
键,此时最后执行的那条 Ex 命令就会被填充到命令行上。再接着按 <Up>
键的话,就可以回到更早的 Ex 历史命令;按 <Down>
键的话,则会沿相反方向滚动。
Vim 缺省会记录最后 20 条命令,对内存越发便宜的现代计算机来说,保存更多历史命令只是小菜一碟,因此我们可以修改 ‘history’ 选项,以提高其保存的上限。
你可以试着把下面这行内容加入 vimrc 文件:
set history=200
8.8、运行Shell命令
不用离开 Vim 就能方便地调用外部程序。更棒的是,我们还可以把缓冲区的内容作为标准输入发送给一个外部命令, 或是把外部命令的标准输出导入到缓冲区里。
在 Vim 的命令行模式中,给命令加一个叹号前缀就可以调用外部程序。例如,如果想查看当前目录的内容,可以运行下面的命令:
:!ls
在 Vim 的命令行中,符号 %
代表当前文件名 。在运行那些操作当前文件的外部命令时,我们可以使用它。例如,如果我们正在编辑某个 脚本 文件,那么可以用下面的方式执行此文件:
:!sh %
:!{cmd}
这种语法适用于执行一次性命令,但是如果想在 shell 中执行几条命令要怎么做?对这种情况,可以执行 Vim 的 :shell
命令来启动一个交互的 shell 会话。用 exit 命令可以退出此 shell 并返回 Vim。
把缓冲区内容作为标准输入或输出:
在用 :!{cmd}
时,Vim 会回显 {cmd} 命令的输出。如果命令的输出很少或没有输出,这工作得很好;但如果命令会产生大量输出,这样回显用处不大。另外一种做法是我们可以用 :read !{cmd}
命令,把 {cmd} 命令的输出读入当前缓冲区中。
:read !{cmd}
命令让我们把命令的标准输出重定向到缓冲区。正如你所期望的一样, :write !{cmd}
做相反的事。它把缓冲区内容作为指定 {cmd} 的标准输入。
使用外部命令过滤缓冲区内容:
当给定一个范围时,:!{cmd}
命令就具有了不同的含义。由[range]所指定的行会传给{cmd} 作为标准输入,然后又会用 {cmd} 的输出覆盖 [range]内原本的内容。
换一种说法就是 [range] 内的文本会被指定的 {cmd} 进行过滤。Vim 把过滤器定义为 “一个由标准输入读取文本, 并对其进行某种形式的修改后输出到标准输出的程序” 。
作为演示,我们将用外部的 sort 命令对下列 CSV 文件中的记录进行排序:
first name,last name,email
john,smith,john@example.com
drew,neil,drew@vimcasts.org
jane,doe,jane@example.com
我们想基于第二个字段“姓氏”来重排这些记录。我们可以用 -t’,’
参数告诉sort 命令,这些记录以逗号分隔,然后再用 -k2
参数指定按第二个字段进行排序。
因为文件的第一行是标题信息,我们想把它们保留在文件顶部,因此需要用范围 :2,$
把它排除在排序范围之外。下列命令将完成我们想要的功能:
:2,$!sort -t',' -k2#结果:1 first name,last name,email2 jane,doe,jane@example.com3 drew,neil,drew@vimcasts.org4 john,smith,john@example.com
总结:
命令 | 用途 |
---|---|
:shell | 启动一个 shell (输入 exit返回 Vim) |
:!{cmd} | 在 shell 中执行 {cmd} |
:read !{cmd} | 在 shell 中执行 {cmd} ,并把其标准输出插入到光标下方 |
:[range]write !{cmd} | 在 shell 中执行 {cmd} ,以 [range] 作为其标准输入 |
:[range]!{filter} | 使用外部程序 {filter} 过滤指定的 [range] |
相关文章:
Linux运维——Vim技巧一
Vim技巧 一、优化重复操作1.1、 . 命令1.2、* 命令1.3、重复修改示例 二、删除单词(daw)三、对数字做算数运算四、操作符与动作五、插入模式5.1、插入模式下删除5.2、返回普通模式5.3、插入-普通模式5.4、不离开插入模式,粘贴寄存器中的文本5…...
第一节:OpenCV 基础入门-简介与环境搭建
一、OpenCV 是什么?为什么值得学习? OpenCV(Open Source Computer Vision Library) 是一个开源的计算机视觉和机器学习库,由英特尔实验室于1999年发起,现已成为全球计算机视觉领域最广泛使用的工具之一。它…...
前端面经-VUE3篇(二)--vue3组件知识(一)组件注册、props 与 emits、透传、插槽(Slot)
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成一个层层嵌套的树状结构: 一、注册 Vue 组件本质上是一个可以复用的 自定义 HTML 元素,为了在其他组件中使用一…...
Python的简单练习
两数的最大公约数 def gcd(a, b):while b ! 0:a, b b, a % breturn a# 示例 a 36 b 60 print(f"{a} 和 {b} 的最大公约数是: {gcd(a, b)}") while b ! 0: while:是 Python 的 循环语句,意思是“当...的时候一直重复做某事”。 b ! 0&am…...
ipvsadm,是一个什么工具?
1. ipvsadm 是什么? ipvsadm(IP Virtual Server Administration)是 Linux 内核中 IPVS(IP Virtual Server) 模块的管理工具,用于配置和监控内核级的负载均衡规则。它是 Kubernetes 中 kube-proxy 在 IPVS …...
QT6 源(72):阅读与注释单选框这个类型的按钮 QRadioButton,及各种属性验证,
(1)按钮间的互斥: (2)源码来自于头文件 qradiobutton . h : #ifndef QRADIOBUTTON_H #define QRADIOBUTTON_H#include <QtWidgets/qtwidgetsglobal.h> #include <QtWidgets/qabstractbutton.h>…...
Qt 中实现观察者模式(Observer Pattern)
在 Qt 中实现**观察者模式(Observer Pattern)通常利用其内置的信号与槽(Signals & Slots)**机制,这是最符合 Qt 设计哲学的方式。以下是详细实现方法和关键点: —### 1. 观察者模式的核心思想- Subject(被观察者):维护一个观察者列表,在状态变化时通知观察者。- …...
Vue3源码学习5-不使用 `const enum` 的原因
文章目录 前言✅ 什么是 const enum❌ 为什么 Vue 3 不使用 const enum1. 📦 **影响构建工具兼容性**2. 🔁 **难以做模块间 tree-shaking**3. 🧪 **调试困难**4. 📦 **Vue 是库,不掌控用户配置** ✅ 官方推荐做法&…...
自己部署后端,浏览器显示久久未响应
CIDER地址写错了,应该要写成0.0.0.0/0 。。。。...
【RocketMQ NameServer】- NettyEventExecutor 处理 Netty 事件
文章目录 1. 前言2. NettyEventExecutor 线程3. NettyEvent 是怎么来的4. NettyEventExecutor 线程处理不同事件的逻辑4.1 IDLE\CLOSE\EXCEPTION - onChannelIdle4.2 CONNECT - onChannelConnect 5. 小结 本文章基于 RocketMQ 4.9.3 1. 前言 【RocketMQ】- 源码系列目录 上一…...
JAVA刷题记录: 递归,搜索与回溯
专题一 递归 面试题 08.06. 汉诺塔问题 - 力扣(LeetCode) class Solution {public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {dfs(A, B, C, A.size());}public void dfs(List<Integer> a, List<In…...
【进阶】C# 委托(Delegate)知识点总结归纳
1. 委托的基本概念 定义:委托是一种类型安全的函数指针,用于封装方法(静态方法或实例方法)。 核心作用:允许将方法作为参数传递,实现回调机制和事件处理。 类型安全:委托在编译时会检查方法签…...
推理能力:五一模型大放送
--->更多内容,请移步“鲁班秘笈”!!<--- 近日人工智能领域迎来了一波密集的模型发布潮,多家科技巨头和研究机构相继推出了具有突破性特点的AI模型。这些新模型在参数规模、计算效率、多模态能力以及推理能力等方面都展现出…...
数据库=====
创建数据库 1.直接创建数据库 语法:CREATE DATABASE [IF NOT EXISTS] 数据库名 ——[]表示内部内容可省略 2.指定字符集和排序规则方式创建数据库 语法:CREATE DATABASE[IF NOT EXISTS] 数据库名 CHARACTER SET 字符集 COLLATE 排序规则 示例:…...
VITA STANDARDS LIST,VITA 标准清单下载
VITA STANDARDS LIST,VITA 标准清单下载 DesignationTitleAbstractStatusVMEbus Handbook, 4th EditionA users guide to the VME, VME64 and VME64x bus specifications - features over 70 product photos and over 160 circuit diagrams, tables and graphs. The…...
npm pnpm yarn 设置国内镜像
国内镜像 常用的国内镜像: 淘宝镜像 https://registry.npmmirror.com 腾讯云镜像 https://mirrors.cloud.tencent.com/npm/ 华为云镜像 https://repo.huaweicloud.com/repository/npm/ CNPM(阿里系) https://r.cnpmjs.org/ 清华…...
互联网大厂Java面试:从Spring到微服务的技术探讨
场景:互联网大厂Java求职者面试 在一家知名的互联网大厂面试中,面试官王严肃正在面试一位名叫谢飞机的程序员。谢飞机以其独特的幽默感而闻名,但在技术面前,他的能力能否得到认可呢? 第一轮提问:核心技术…...
[machine learning] Transformer - Attention (二)
本文介绍带训练参数的self-attention,即在transformer中使用的self-attention。 首先引入三个可训练的参数矩阵Wq, Wk, Wv,这三个矩阵用来将词向量投射(project)到query, key, value三个向量上。下面我们再定义几个变量: import torch inpu…...
Java多语言DApp质押挖矿盗U源码(前端UniApp纯源码+后端Java)
内容: 这款Java多语言DApp质押挖矿盗U源码提供了完整的前端与后端开发框架,适用于区块链应用开发。系统包括: 前端源码(UniApp):采用UniApp开发,跨平台支持iOS、Android及H5。界面简洁…...
如何解决 403 错误:请求被拒绝,无法连接到服务器
解决 403 错误:请求被拒绝,无法连接到服务器 当您在浏览网站或应用时,遇到 403 错误,通常会显示类似的消息: The request could not be satisfied. Request blocked. We can’t connect to the server for this app o…...
CGI(Common Gateway Interface)协议详解
CGI(通用网关接口)是一种标准化的协议,定义了 Web服务器 与 外部程序(如脚本或可执行文件)之间的数据交互方式。它允许服务器动态生成网页内容,而不仅仅是返回静态文件。 1. CGI 的核心作用 动态内容生成&a…...
HybridCLR 详解:Unity 全平台原生 C# 热更新方案
HybridCLR(原 Huatuo)是 Unity 平台革命性的热更新解决方案,它通过扩展 Unity 的 IL2CPP 运行时,实现了基于原生 C# 的完整热更新能力。下面从原理到实践全面解析这一技术。 一、核心原理剖析 1. 技术架构 原始 IL2CPP 流程&am…...
电脑RGB888P转换为JPEG方案 ,K230的RGB888P转换为JPEG方案
K230开发板本身具备将RGB888P转换为JPEG的能力,但需要正确调用硬件或软件接口。以下是具体分析及解决方案: 一、K230原生支持性分析 1. 硬件支持 K230的NPU(神经网络处理器)和图像处理单元(ISP)理论上支持…...
基于SpringBoot+Vue实现的电影推荐平台功能三
一、前言介绍: 1.1 项目摘要 2023年全球流媒体用户突破15亿,用户面临海量内容选择困难,传统推荐方式存在信息过载、推荐精准度低等问题。传统推荐系统存在响应延迟高(平均>2s)。随着互联网的快速发展,…...
NHANES指标推荐:triglyceride levels
文章题目:Association between triglyceride levels and rheumatoid arthritis prevalence in women: a cross-sectional study of NHANES (1999-2018) DOI:10.1186/s12905-025-03645-y 中文标题:女性甘油三酯水平与类风湿性关节炎患病率之间…...
打印Activity的调用者
有时候我们会发现自己应用中的某个Activity被陌名奇妙的打开了,但是不知道是哪里的代码打开的,此时可以打印Activity的调用堆栈,在Activity的onCreate函数中添加如下代码: Arrays.stream(Thread.currentThread().getStackTrace()…...
深入解析 SqlSugar 与泛型封装:实现通用数据访问层
在现代软件开发中,ORM(对象关系映射)框架的使用已经成为不可或缺的部分,SqlSugar 是一款非常流行且强大的 ORM框架。它不仅提供了简单易用的数据库操作,还具备了高效的性能和灵活的配置方式。为了进一步提升数据库操作…...
普通 html 项目引入 tailwindcss
项目根目录安装依赖 npm install -D tailwindcss3 postcss autoprefixer 初始化生成tailwind.config.js npx tailwindcss init 修改tailwind.config.js /** type {import(tailwindcss).Config} */ module.exports {content: ["./index.html"], //根据自己的项目…...
Go小技巧易错点100例(二十七)
本期分享: 1. Go语言中的Scan函数 2. debug.Stack()打印堆栈信息 3. Go条件编译 正文: Go语言中的Scan函数 在Go语言中,Scan函数是一个强大的工具,它主要用于从输入源(如标准输入、文件或网络连接)读取…...
单细胞测序数据分析流程的最佳实践
单细胞测试数据分析流程是整个论文数据分析过程中相对固定的部分,有一定的标准流程,以下整理了发表论文的相关内容供简要了解,详细内容可以参照2019年发表的综述:Luecken MD, Theis FJ. Current best practices in single-cell RN…...
Elasticsearch:RAG 和 grounding 的价值
作者:来自 Elastic Toms Mura 了解 RAG、grounding,以及如何通过将 LLM 连接到你的文档来减少幻觉。 更多阅读:Elasticsearch:在 Elastic 中玩转 DeepSeek R1 来实现 RAG 应用 想获得 Elastic 认证吗?查看下一期 Elast…...
经典算法 求解台阶问题
求解台阶问题 题目描述 实现一个算法求解台阶问题。介绍如下: 对于高度为 n 的台阶,从下往上走,每一步的阶数为 1、2 或 3 中的一个。问要走到顶部一共有多少种走法。 输入描述 输入一个数字 N: 1 ≤ N ≤ 35表示台阶的高度 …...
伊甸园之东: 农业革命与暴力的复杂性
农业革命的开始 农业革命是人类历史上的第一次重大经济和社会变革,标志着人们从狩猎采集转向农耕。 该变革虽然进展缓慢,却彻底改变了人类的生活方式和社会结构。狩猎采集社会的特征 狩猎采集者生活在小规模、低密度的部落中,依赖于不稳定的自…...
MCP多智能体消息传递机制(Message Passing Between Agents)
目录 🚀 MCP多智能体消息传递机制(Message Passing Between Agents) 🌟 为什么要引入消息传递机制? 🏗️ 核心设计:Agent间消息传递模型 🛠️ 1. 定义标准消息格式 Ὦ…...
Deformable DETR模型解读(附源码+论文)
Deformable DETR 论文链接:Deformable DETR: Deformable Transformers for End-to-End Object Detection 官方链接:Deformable-DETR(这个需要在linux上运行,所以我是用的是mmdetection里面的Deformable DERT,看了一下源码基本是…...
游戏引擎学习第255天:构建配置树
为今天的内容设定背景 今天的任务是构建性能分析(profiling)视图。 目前来看,展示性能分析图形本身并不复杂,大部分相关功能在昨天已经实现。图形显示部分应该相对直接,工作量不大。 真正需要解决的问题,是…...
JavaScript性能优化实战之调试与性能检测工具
在进行 JavaScript 性能优化时,了解和使用正确的调试与性能检测工具至关重要。它们能够帮助我们识别性能瓶颈,精确定位问题,并做出有针对性的优化措施。本文将介绍一些常见的调试和性能检测工具,帮助你更好地分析和优化你的 JavaScript 代码。 1️⃣ Chrome DevTools Chro…...
C#VisionMaster算子二次开发(非方案版)
前言 在网上VisionMaster的教程通常都是按照方案执行的形式,当然海康官方也是推荐使用整体方案的形式进行开发。但是由于我是做标准设备的,为了适配原有的软件框架和数据结构,就需要将特定需要使用的算子进行二次封装。最直接的好处是&#…...
计算机总线系统入门:理解数据传输的核心
一、总线系统简介:计算机内部的交通网络 在计算机系统中,总线是指连接各个组件的一组共享信号线或传输通道,用于在系统内不同的硬件模块之间传递数据、地址、控制信号等信息。它类似于交通系统中的道路,帮助计算机各个部件&#…...
【Linux】Petalinux驱动开发基础
基于Petalinux做Linux驱动开发。 部分图片和经验来源于网络,若有侵权麻烦联系我删除,主要是做笔记的时候忘记写来源了,做完笔记很久才写博客。 专栏目录:记录自己的嵌入式学习之路-CSDN博客 目录 1 一个完整的Linux系统(针对Zynq) 1.1 PS部分 1.2 PL部分(若…...
提升办公效率的PDF转图片实用工具
软件介绍 这款专注于PDF文档处理的工具功能单一但实用,能够将PDF文件内容智能提取并自动拼接成长图,为用户提供便捷的图片化文档处理方案,无需复杂设置即可轻松上手。 简洁直观的用户界面 软件界面设计简洁清爽,没有任何多余…...
动态库与ELF加载
目录 动态库 ELF格式 ELF和后缀的区别 什么是目标文件 ELF文件中的地址--虚拟地址 动静态库和可执行文件 动态库ELF加载 为什么编译时静态库需要指定库?而运行时不需要指定库的,但是动态库需要呢? 总结: 动态库 动态库制作需要的.o文件需要使…...
算法每日一题 | 入门-顺序结构-数字反转
数字反转 题目描述 输入一个不小于 且小于 ,同时包括小数点后一位的一个浮点数,例如 ,要求把这个数字翻转过来,变成 并输出。 输入格式 一行一个浮点数 输出格式 一行一个浮点数 输入输出样例 #1 输入 #1 123.4输出 #1 …...
ROS2学习笔记|实现订阅消息并朗读的详细步骤
本教程将详细介绍如何使用 ROS 2 实现一个节点订阅另一个节点发布的消息,并将接收到的消息通过 espeakng 库进行朗读的完整流程。以下步骤假设你已经安装好了 ROS 2 环境(以 ROS 2 Humble 为例),并熟悉基本的 Linux 操作。 注意&…...
【Hot 100】 146. LRU 缓存
目录 引言LRU 缓存官方解题LRU实现📌 实现步骤分解步骤 1:定义双向链表节点步骤 2:创建伪头尾节点(关键设计)步骤 3:实现链表基础操作操作 1:添加节点到头部操作 2:移除任意节点 步骤…...
web应用开发说明文档
工程目录结构 FACTORY--bin #网络流可执行程序 参考后文1.1部分文字说明webrtc-streamer--deployment #部署相关的配置--mysql #参考1.3 mysql数据库详细说明--conf #存放mysql的配置文件--data #存放pem加密…...
快速搜索与管理PDF文档的专业工具
软件介绍 在处理大量PDF文档时,专业的文档管理工具能显著提升工作效率。这款工具能够帮助用户快速检索PDF内容,并提供了便捷的合并与拆分功能,让复杂的PDF操作变得简单高效。 多文件内容检索能力 不同于传统PDF阅读器的单文件搜索局…...
在GPU集群上使用Megatron-LM进行高效的大规模语言模型训练
摘要 大型语言模型在多个任务中已取得了最先进的准确率。然而,训练这些模型的效率仍然面临挑战,原因有二:a) GPU内存容量有限,即使在多GPU服务器上也无法容纳大型模型;b) 所需的计算操作数量可能导致不现实的训练时间。因此,提出了新的模型并行方法,如张量并行和流水线…...
NocoDB:开源的 Airtable 替代方案
NocoDB:开源的 Airtable 替代方案 什么是 NocoDB?NocoDB 的主要特点丰富的电子表格界面工作流自动化应用商店程序化访问 NocoDB 的应用场景使用 Docker 部署 NocoDB1. 创建数据目录2. 运行 Docker 容器3. 访问 NocoDB 注意事项总结 什么是 NocoDB&#x…...
关于Python:7. Python数据库操作
一、sqlite3(轻量级本地数据库) sqlite3 是 Python 内置的模块,用于操作 SQLite 数据库。 SQLite 是一个轻量级、零配置的关系型数据库系统,整个数据库保存在一个文件中,适合小型项目和本地存储。 SQLite 不需要安装…...