【vim】常用總結

Nemo&發表於2021-06-19

簡介

什麼是vim?

Linux下兩大編輯神器之一 vim

  • Linux/Unix下使用最多的編輯器
  • vi的改進版
  • 可能是最難上手的編輯器之一

為什麼要學習vim?

都21世紀了,為什麼還需要學習vim?

  • 只要你用伺服器,vim就是個必備技能
  • 後端和運維工程師必須掌握的編輯器,不然伺服器上操作很尷尬
  • 可以純鍵盤編輯,提升編輯效率

初識vim

據說Stackoverflow幫助過上百萬開發者退出過vim!

  1. 輸入vim命令進入vim
  2. 進去之後似乎沒法輸入東西,起始預設進去是normal模式
  3. 使用:q可以退出vim

開始編輯

進入編輯模式就可以像其他編輯器一樣編輯了

  • 使用i(insert)進入編輯模式,開始輸入文字
  • i表示(insert),a(append),o(open a line below)
  • 使用Esc可以退出編輯模式,又回到normal模式。
  • 使用:wq儲存退出

    w(write),q(quit)

insert -> normal:使用Esc

normal -> insert:(小寫是操作字元大寫是操作

  • i:(insert)表示進入insert編輯模式後,在當前字元的前面插入

    insert

  • a:(append)表示進入insert編輯模式後,在當前字元後附加插入

    append

  • o:(open a line below)表示進入insert編輯模式後,在當前行下新啟一行插入

    append

  • I:(insert before line)表示在當前行的最前面插入

    insert before line

  • A:(append after line)表示在當前行的最後附加插入

    append after line

  • O:(append a line above)表示在當前行上新啟一行插入

    append a line above

儲存:

  • :w:(write)寫入,儲存

    write

  • :q:(quit)退出

    quit

  • :wq:(write and quit)儲存並退出

    write and quit

vim,為什麼你有這麼多模式

怪異的vim模式

vim和其他很多編輯器的區別在於多種模式

  • 進入vim預設是normal(普通)模式?為什麼不是編輯模式呢?
  • 使用a(append)、i(insert)等進入編輯模式
  • 還有cmd命令模式和v(visual)視覺化模式

為什麼進入vim不能插入-normal模式

奇怪的是,為什麼vim進入之後不像其他編輯器一樣直接插入?

  • 進入Vim預設是normal(普通)模式。使用Esc從插入回到普通模式

  • 普通模式下可以進行各種命令操作和移動

  • 大部分情況下你是在瀏覽而不是編輯,所以Vim預設是normal

Insert(插入)模式

插入模式下Vim可以直接編輯,和其他編輯器一樣

  • 使用i(insert) 、a(append) 、o (open a line below)進入插入模式

  • 使用Esc退出插入模式到normal模式

Command(命令)模式

Normal模式下輸入:之後執行命令,比如儲存退出:wq一氣呵成

  • 顧名思義,執行Vim命令,比如儲存:w,退出:q
  • 比如分屏::vs (vertical split),:sp(split)
  • 比如使用::% s/foo/bar/g全域性替換

    %:表示本檔案全部文字
    g:表示全域性替換

Visual(可視)模式

Visual模式一般用來塊狀選擇文字(類比使用Alt+滑鼠拖動)

  • Normal模式下使用v進入visual選擇
  • 使用V選擇行
  • 使用ctrl+v進行方塊選擇

vim編輯小技巧

如何快速糾錯(insert模式下)

程式碼就像人生,總是經常出錯,需要我們快速修正

  • 進入Vim之後首先使用a/i/o之一進入插入模式
  • ctrl+h 刪除上一個字元,ctrl+w刪除上一個單詞,ctrl+u刪除當前行

快速切換insert和normal模式

很多鍵盤的鍵位設定, Esc按起來不方便,不方便切換到normal

  • 使用Ctrl+c代替Esc (但是可能會中斷某些外掛)或者ctrl+[
  • gi 快速跳轉到你最後一次編輯的地方並進入插入模式
  • 在配置章節我們會講解如何對映方便的快捷鍵來替代Esc

vim快速移動大法

反人類的hjkl

眾所周知,vim normal模式使用了反人類的hjkl來移動

  • 據說編輯器作者在編寫vim的時候鍵盤還沒流行上下左右按鍵
  • h(左) ,j(下) ,k(上) ,l(右) ,移動也不會讓手指脫離主鍵盤區
  • 相信我,逼著自己使用hjkl一段時間之後,你會體會到它非常有效率

使用hjkl

只需要幾天的適應,你就可以熟練使用hjkl來移動
【vim】常用總結

在單詞之間“飛舞”(normal模式下)

在單詞之間移動是我們瀏覽操作中最常使用的命令

  • w/W移到下一個word/WORD開頭。e/E 下一個word/WORD尾
  • b/B 回到上一個word/WORD開頭,可以理解為backword
  • word指的是以非空白符分割的單詞, WORD以空白符分割的單詞

行間搜尋移動

同一行快速移動的方式其實是搜尋一個字元並且移動到該字元

  • 使用f{char}可以移動到char字元上,t{char}移動到char的前一個字元

    f:(find)
    t:(until)

    可以配合刪除使用,如 dt)從當前字元開始刪除直到)。

  • 如果第一次沒搜到,可以用分號(;)/逗號(,)繼續搜該行下一個/上一個
  • 大寫的F{char}表示反過來搜前面的字元

vim水平移動

如何快速移動到一行的行首或者行尾呢?

  • 0移動到行首第一個字元,^移動到第一個非空白字元

    ^:在正規表示式中,表示行的開頭

  • $移動到行尾,g_ 移動到行尾非空白字元

    $:在正規表示式中,表示行的結尾

  • 記住常用的0和$就可以滿足日常需求了

Vim垂直移動

我們還可以在句子(sentence)和段落(paragraph)間移動(不常用)

  • 使用括號()在句子間移動,你可以用:help (來檢視幫助
  • 使用{}在段落之間移動
  • 外掛章節我們會講如何用easy-motion外掛移動,這些命令就都不用記憶啦

Vim頁面移動

翻頁命令太多, 2/8定律,記住常見的就能滿足需求

  • gg/G移動到檔案開頭和結尾,你可以使用ctrl+o快速返回
  • nG移動到檔案第n行,可以配合命令使用,如 dnG表示刪除到第n行
  • H/M/L跳轉到螢幕的開頭(Head),中間(Middle)和結尾(Lower)
  • Ctrl+uctrl+f上下翻頁(upword/forward)。zz 把螢幕置為中間

什麼是vim的text object

Text Object(文字物件)

如果你學過主流的程式語言,一定知道物件導向程式設計

  • Vim裡文字也有物件的概念,比如一個單詞,一段句子,一個段落
  • 很多其他編輯器經常只能操作單個字元來修改文字,比較低效
  • 通過操作文字物件來修改要比只操作單個字元高效

文字物件操作方式

之前我們已經使用過文字物件了,回憶下dw (刪除一個單詞)

  • [number] <command> [text object]
  • 或者 <command> [number] [text object]
  • number表示次數,command是命令,d(delete),c(change),y(yank)
  • text object是要操作的文字物件

    比如單詞w(word),句子s(sentence),段落p(paragraph),標籤t(tag)

通過示例來學習

  • iw表示inner word,如果鍵入viw命令,那麼首先v將進入選擇模式,然後iw將選中當前單詞。
  • aw表示around word,它不但會選中當前單詞,還會包含當前單詞之後的空格。

以下例項中的[]表示作用範圍

  • iw
    This is a [test] sentence.

  • aw
    This is a [test ]sentence.

  • iw
    This is a [...test...] sentence.

  • aw
    This is a [...test... ]sentence.

  • is
    ...sentence. [This is a sentence.] This...

  • as
    ...sentence. [This is a sentence. ]This...
    End of previous paragraph.

  • ip
    [This is a paragraph. It has two sentences.]
    The next.
    End of previous paragraph.

  • ap
    [This is a paragraph. It has two sentences.
    ]The next.

  • ip
    <xml>[hello world]</xml>

  • ap
    [<xml>hello world</xml>]

  • i( or i)
    1*([2+3])

  • a( or a)
    1*[(2+3)]

  • i< or i>
    The <[tag]>

  • a< or i>
    The [<tag>]

  • i{ or i}
    some {[code block]}

  • a{ or a}
    some [{code block}]

  • i[ or i]
    some [[code block]]

  • a[ or a]
    some [[code block]]

  • i"
    The "[best]"

  • a"
    The ["best"]

  • i'
    The '[best]'

  • a'
    The ['best']

vim快速增刪改查

數字+命令表示多次執行命令,如 2dw

或者 命令+數字+物件,如 d2w

Vim增加字元

增加字元就是進入插入模式,開始敲鍵盤吧

  • 進入插入模式編輯文字
  • 使用a/i/o進入插入模式
  • 還記得A/I/O是什麼作用麼?

Vim快速刪除

如何快速刪除一個字元或者單詞呢 ?

  • Vim在normal模式下使用x快速刪除一個字元
  • 使用d (delete)配合文字物件快速刪除一個單詞 daw (delete around word)

    dw:預設為daw

  • dx都可以搭配數字來執行多次

Vim快速修改

相比刪除,更常用修改,一般是刪除之後改成我們期望的文字

  • normal 模式下,常用有三個, r (replace),c (change),s(substitute)
  • 使用r可以替換一個字元。
  • 使用s替換並進入插入模式
  • 使用c配合文字物件,我們可以快速進行修改整個文字物件。如ci"ciw

Vim查詢

查詢單詞也是一個常用操作

  • 使用/或者?進行前向或者反向搜尋
  • 使用n/N跳轉到下一個或者上一個匹配

    next

  • 使用*或者#進行當前單詞的前向和後向匹配

    *:在正規表示式中表示出現0次或多次。

vim如何搜尋替換

Vim替換命令

substitute命令允許我們查詢並且替換掉文字,並且支援正則式

  • :[range]s(substitute)/{pattern}/{string}/[flags]
  • range:表示範圍。比如:10,20表示10-20行,%表示本檔案中所有的行。
  • pattern:是要替換的模式,正規表示式
  • string:是替換後文字

注意:此命令不僅能用來替換,還能用來刪除!如 刪除所有星號*,命令為:%s/\*//g

替換範圍range

你可以在命令之前設定一個範圍。以下是一些方法:

  • m, n:表示範圍為從第 m 行到第 n 行
  • 1,:表示範圍為從第 1 行到當前行

    注意:第一行的行號是已知的,行號為 1;而最後一行的行號是未知的,所以我們只用給最後一行分配一個符號代表即可。

除了數字之外,還可以使用這些符號作為範圍:

  • .:表示當前行,可以省略。如 .,3表示當前行到第 3 行。
  • $:表示檔案中的最後一行。如 3,$表示第 3 行到最後一行。
  • +n:表示當前行的後 n 行。3,+1 或者 3,.+1.可以用或不用,表示第 3 行到當前行後的 1 行。
  • %:表示本檔案中所有的行。

    在 Vim 中,%通常表示整個檔案。

示例:

  • :1,5g/console/d 匹配第 1 行和第 5 行之間的字串"console"並刪除它們。
  • :,5g/console/d 如果逗號之前沒有地址,則從當前行開始。它在當前行和行 5 之間尋找字串"console"並刪除它們。
  • :3,g/console/d 如果逗號後沒有地址,則以當前行結束。它尋找第 3 行和當前行之間的字串"console"並刪除它們。
  • :3g/console/d 如果僅傳遞一個沒有逗號的地址,則它只執行第 3 行的命令。它檢視第 3 行,如果字串"console",它將刪除它。

替換模式pattern

請參照正規表示式

替換標誌位flag

Flags有幾個常用的標誌

  • 預設為空,表示僅替換一行上的第一個匹配項
  • g(global) 表示改變一行上的每個符合條件的匹配項,全域性範圍內執行

    global

  • c(confirm)表示確認 ,可以確認或者拒絕修改

    confirm

  • n(number)報告匹配到的次數而不替換,可以用來查詢匹配次數

    number

&    Reuse the flags from the previous substitute command. Must be passed as the first flag.
g    Replace all matches in the line.
c    Ask for substitution confirmation.
e    Prevent error message from displaying when substitution fails.
i    Perform case insensitive substitution
I    Perform case sensitive substitution

There are more flags that I do not list above. To read about all the flags, check out .:h s_flags

Vim如何對匹配行執行命令

global命令實際上是分成兩步執行:首先掃描[range]指定範圍內的所有行,給匹配{pattern}的行打上標記;然後依次對打有標記的行執行{command}命令,如果被標記的行在對之前匹配行的命令操作中被刪除、移動或合併,則其標記自動消失,而不對該行執行{command}命令。{command}可以是一個ex命令,也可以是用|分隔的多個ex命令,這樣我們就可以對被標記行,或從標記行定址到的行進行多種不同的操作。

全域性命令執行步驟:

  1. 掃描[range]指定範圍內的所有行,給匹配{pattern}的行打上標記;
  2. 依次對打有標記的行執行{command}命令,如果被標記的行在對之前匹配行的命令操作中被刪除、移動或合併,則其標記自動消失,而不對該行執行{command}命令。

全域性命令(The Global Command)

Vim 的全域性命令用於同時在多行上執行命令列命令。

特別注意:全域性命令是對匹配的,來進行行操作,操作物件是整個行,而不僅僅是匹配的那一部分字元。

全域性命令具有以下語法:

  • :[range]g(global)/{pattern}/{command}
    該模式匹配包含該模式的所有行,類似於 substitute 命令中的模式。該命令可以是任何命令列命令。
    全域性命令的工作方式是對匹配pattern的每一行執行command命令。

反向全域性命令

要在不匹配的行上執行全域性命令,可以執行:

  • :[range]g(global)!/{pattern}/{command}

  • :v/{pattern}/{command}

通過示例來學習

如果你有下列表示式:

const one = 1;
console.log("one: ", one);

const two = 2;
console.log("two: ", two);

const three = 3;
console.log("three: ", three);

要刪除所有包含“控制檯”的行,您可以執行::g/console/d

結果:

const one = 1;

const two = 2;

const three = 3;

如果使用反向全域性命令,則它將刪除所有不包含“console”的行

vim如何編輯命令(如 貼上一個命令)

問題速成解決方式

怎樣在vim的命令列中拷貝,貼上?

在vim的普通模式下,輸入:進入命令列模式,我們可以在螢幕下方輸入我們想讓vim執行的命令。

有時候,我們需要在命令列中執行拷貝貼上,例如 我們想要查詢替換正文中的特殊字元,但我們又不知道怎麼在命令列中輸入這個特殊字元。
查詢還好說,不一定非要在命令列中輸入這個特殊字元,只要把游標放在這個特殊字元上,按*向下查詢,按#向上查詢。
但是如果我們要替換這個特殊字元,就必須在命令列中輸入它了,但是又不知道怎麼輸入這個特殊字元,怎麼辦?
答案是拷貝貼上,先在正文中複製這個字元,然後貼上在命令列中。

可是怎麼在命令列中實現貼上呢?別急,介紹一個好東西:命令列視窗。
在正常模式下,輸入q:,就開啟了命令列視窗。
命令列視窗中都是我們輸入過的命令,在這個視窗中我們可以像編輯正文一樣編輯新的命令,或修改舊的命令。
因為像編輯普通緩衝區一樣,自然可以拷貝貼上了。
編輯完之後,在想要執行的命令上按回車,就ok了。
命令執行後,命令列視窗也自動關閉了。

命令列視窗

“歷史記錄”視窗顯示以前使用的命令列命令的列表,但是不能從“歷史記錄”視窗執行該命令。若要在瀏覽時執行命令,請使用命令列視窗。有三個不同的命令列視窗:

q:    Command-line window
q/    Forward search window
q?    Backward search window

q(quick,快速的):

  • q::命令列命令編輯,可以看到所有使用過的命令的歷史記錄
  • q/:向前搜尋命令編輯,可以看到所有使用過的向前搜尋命令的歷史記錄
  • q?:向後搜尋命令編輯,可以看到所有使用過的向後搜尋命令的歷史記錄

執行 q: 以啟動命令列命令的命令列視窗。Vim 將在螢幕底部啟動一個新視窗。您可以使用 Up 或 Ctrl-p 鍵向上遍歷,使用 Down 或 Ctrl-n 鍵向下遍歷。如果按下回車,Vim 將執行該命令。若要退出命令列視窗,請按 Ctrl-c、 Ctrl-w c 或鍵入: quit。

類似地,要啟動用於搜尋的命令列視窗,可以執行向前搜尋的 q/ 和向後搜尋的 q?

vim複製貼上與暫存器的使用

Vim Normal模式複製貼上

初學者會感覺Vim複製貼上比較奇怪,先從normal模式學習

  • normal模式下複製貼上分別使用y(yank)和p(put) ,剪貼d(delete)和p(put)
  • 我們可以使用v(visual)命令選中所要複製的地方,然後使用p貼上
  • 配合文字物件:比如使用yiw複製一個單詞, yy複製一行
    Vim裡的剪貼(Cut)複製(copy)貼上(paste)
    分別是delete/yank/put

Insert模式下的複製貼上

很多人會使用滑鼠進行選中,然後使用ctrl+v或者cmd+v貼上

  • 這個和其他的文字編輯器差不多,但是貼上程式碼有個坑
  • 很多人在vimrc中設定了autoindent ,貼上Python程式碼縮排錯亂
    這個時候需要使用:set paste:set nopaste解決

  • Ctrl-r {register}:可以貼上暫存器{register}中的內容。

    系統剪貼簿暫存器為 "+

什麼是Vim的暫存器?

你有沒有好奇? Vim在normal模式下複製/剪貼的內容去了哪?

  • Vim裡操作的是暫存器而不是系統剪貼簿,這和其他編輯器不同
  • 預設我們使用d刪除或者y複製的內容都放到了“無名暫存器”
  • x刪除一個字元放到無名暫存器,然後p貼上,可以調換倆字元

深入暫存器(register)

Vim不使用單一剪貼簿進行剪貼、複製與貼上,而是多組暫存器

  • 通過"{register} 字首可以指定暫存器,不指定預設用無名暫存器
  • 比如使用"ayiw複製一個單詞到暫存器a中,"bdd刪除當前行到
    暫存器b中
  • Vim中""表示無名暫存器,預設使用。""p其實就等同p

其他常見暫存器

除了有名暫存器a-z, Vim中還有一些其他常見暫存器

  • 複製專用暫存器"0:使用y複製文字同時會被拷到複製暫存器0
  • 系統剪貼簿 "+:可以在複製前加上 "+ 複製到系統剪貼簿
  • 其他一些暫存器比如 "%:當前檔名,".:上次插入的文字

:set clipboard=unnamed:可以讓你直接複製貼上系統剪貼簿內容

強大的vim巨集(macro)

從需求說起

從一個需求說起,給多行url連結加上雙引號,你會怎麼做呢?
【vim】常用總結

什麼是Vim巨集(macro)

很多使用Vim很久的Vimer都沒用過巨集

  • 巨集可以看成是一系列命令的集合
  • 我們可以使用巨集「錄製」一系列操作,然後用於「回放」
  • 巨集可以非常方便地把一系列命令用在多行文字上

如何使用巨集

巨集的使用分為錄製和回放,是不是有種拍電影的感覺

  • Vim使用q{register}來錄製,同時也是q結束錄製

    quick

  • 使用q{register}選擇要儲存的暫存器,把錄製的命令儲存其中
  • 使用q結束錄製
  • 使用@{register}回放暫存器中儲存的一系列命令

用巨集解決剛才的問題

現在你知道巨集怎麼用了,如何解決剛才的問題呢?

  • 先給一行加上雙引號,然後再回放到其他所有行
  • 我們先使用q{register}開始錄製,給一行加上雙引號,之後使用q退出
  • 在剩下的所有行中回放錄製的巨集(使用可視模式選中,然後使用巨集)

    需要使用v可視模式選擇所有url,再使用:normal在命令模式下使用normal模式下的命令。

不使用巨集解決剛才的問題

可以通過可視模式選擇所有url
可以使用:normal,在命令模式使用normal模式的命令
:normal I"
:normal A"

vim補全大法

什麼是補全?

只要手速快,補全什麼的都是浮雲

  • 補全是根據當前環境上下文由編輯器“猜”你想輸入的東西
  • 比如補全一個單詞、檔名,或者程式碼中的函式名、變數名等
  • Vim中提供了多種補全功能,還可以由外掛擴充功能實現程式碼補全

vim中常用的補全

Vim中有很多種補全的方式

命令 補全型別
<C-n> 普通關鍵字
<C-x><C-n> 當前緩衝區關鍵字
<C-x><C-i> 包含檔案關鍵字
<C-x><C-]> 標籤檔案關鍵字
<C-x><C-k> 字典查詢
<C-x><C-l> 整行補全
<C-x><C-f> 檔名補全
<C-x><C-o> 全能(Omni)補全

常見的三種補全型別

記不住不用擔心,根據筆者經驗,最常用的就三種

  • 使用ctrl+n和ctrl+p補全單詞
  • 使用ctrl+x和ctrl+f補全檔名
  • 使用ctrl+x和ctrl+o補全程式碼,需要開啟檔案型別檢查,安裝外掛

vim多檔案操作

Buffer Window Tab

我們先來介紹幾個多檔案操作相關的概念

  • Buffer是指開啟的一個檔案的記憶體緩衝區
  • 視窗是Buffer視覺化的分割區域
  • Tab可以組織視窗為一個工作區
    【vim】常用總結

Buffer

Buffer-什麼是緩衝區?

  • Vim 開啟一個檔案後會載入檔案內容到緩衝區
  • 之後的修改都是針對記憶體中的緩衝區,並不會直接儲存到檔案
  • 直到我們執行:w (write)的時候才會把修改內容寫入到檔案裡

Buffer切換

那如何在buffer之間切換呢?

  • 使用:ls會列舉當前緩衝區,然後使用:b n跳轉到第n個緩衝區
  • :bpre:bnext:bfirst:blast
  • 或者用:b buffer_name加上tab補全來跳轉

Window視窗

視窗是視覺化的分割區域

  • 一個緩衝區可以分割成多個視窗,每個視窗也可以開啟不同緩衝區
  • <Ctrl+w>s 水平分割,<Ctrl+w>v 垂直分割。或者:sp:vs
  • 每個視窗可以繼續被無限分 割(看你螢幕是否夠大)

分割視窗示例

視窗分割效果
【vim】常用總結

如何切換視窗

切換視窗的命令都是使用Ctrl+w(window)作為字首

命令 用途
<C-w>w 在視窗間迴圈切換
<C-w>h 切換到左邊的視窗
<C-w>j 切換到下邊的視窗
<C-w>k 切換到上邊的視窗
<C-w>l 切換到右邊的視窗

如何重排視窗?

重排視窗可以改變視窗的大小:h window-resize檢視文件

命令 用途
<C-w>= 使所有視窗等寬、等高
<C-w>_ 最大化活動視窗的高度
<C-w>| 最大化活動視窗的寬度
[N]<C-w>_ 把活動視窗的高度設為[N]行
[N]<C-w>| 把活動視窗的寬度設為[N]列

Tab(標籤頁)將視窗分組

Tab是可以容納-系列視窗的容器(:h tabpage)

  • Vim的Tab和其他編輯器不太一樣,可以想象成Linux的虛擬桌面
  • 比如一個Tab全用來編輯Python檔案, 一個Tab全是HTML檔案
  • 相比視窗, Tab一般用的比較少, Tab太多管理起來也比較麻煩

Tab(標籤頁)操作

Tab使用不多,簡單瞭解一下常用操作就好

命令 用途
:tabe[edit] {filename} 在新標籤頁中開啟{filename}
<C-w>T 把當前視窗移到一個新標籤頁
:tabc [close] 關閉當前標籤頁及其中的所有視窗
:tabo [only] 只保留活動標籤頁,關閉所有其他標籤頁

Tab(標籤頁)切換操作

如何切換不同的標籤頁,一般建立兩個就好,太多不好操作

Ex命令 普通模式命令 用途
:tabn[next] {N} {N}gt 切換到編號為{N} 的標籤頁
:tabn[next] gt 切換到下一標籤頁
:tabp[previous] gT 切換到上一標籤頁