《Practical Vim》筆記及總結

最近集中讀了幾本關于Vim的書,?以《Practical Vim》最為詳盡,特重新溫習并總結成篇,一來作為復習和練習,二來方便今后備查。

由于主要給自己看,一些特別基本的操作就不在贅述,了解normal mode和insert mode的區別和基本操作,能夠進行基本的增刪改操作即可。

本文示例摘抄翻譯自Drew Neail的《Practical Vim》一書

<C-v>代表Ctrl+v,這里的例子主要適用于linux環境下的vim,對于windows環境,存在快捷命令沖突的情況,需要注意。

主要內容:

  1. Vim的Visual Mode和Command-Line Mode
  2. 文件中、文件之間快速跳轉的技巧
  3. Vim的寄存器、宏命令
  4. Vim的模式匹配、搜索與替換--特別是神奇的global命令

I Visual Mode

可視模式,用于選擇“一塊”內容,并進行相應操作。

命令 作用
v 基于單詞的可視模式
V 基于行的可視模式
<C-v> 基于塊的可視模式
gv 重新選中上次選中的塊
o命令

在進入可是模式后,相關的移動操作依然可用,在移動的同時會選中經過的內容,這里有一個比較有用的移動命令是o,用于跳轉到本次選中區域的另一端——就不用取消重選了。

圖1-1 o命令的選擇演示
用“.”來重復命令

當我們使用.命令來重復之前的執行時,如果是針對一個visual block的執行,重復執行也是再次針對這個block的。
當設置過 :set shiftwidth=4 softtabstop=4 expandtab
這條命令之后,就可以使用>和<命令來進行縮進和取消縮進的操作了。這時候,通過v命令選中后,執行>命令縮進。此時如果要再縮進一級,直接使用.命令進行重復就可以了。

圖1-2 使用.命令重復執行
vitU和gUit

有時候我們需要修改一段html代碼標簽內的東西,強大的Vim給了我們一個直接選中標簽內內容的命令vit,這個命令是v和it命令的組合,其中it命令是一個特殊的motion command。選中后可以執行各類操作命令。比如如下例子:

圖1-3 使用vit命令選中標簽內的內容并進行修改

這樣的操作有一個缺點,由于vit和U命令是分開的,如果我們使用.命令,它并不是完整的重復vit和U命令,而是針對同一行同樣的選中位置部分執行U命令,在上例中,通過j.重復后,在第三行,標簽內的內容會變成THRee而不是我們希望的THREE。為了解決這個問題,我們需要將命令在一條內執行完成。
Vim也提供了相應的方案,使用gUit命令就可以,其中gU是一個操作命令,it是確定命令的執行范圍。

圖1-4 使用gUit命令配合j.批量操作
使用<C-v>來進行列操作

還記得沒用Vim之前,第一次進行列操作用的是UltraEdit,當時覺得好酷炫,鼠標拖一下就可以方便的在一列上進行修改;如今連鼠標都不用,按幾下鍵盤就實現了相同的功能,豈不是更爽!


圖1-5 使用<C-v>進行列操作

這里面用到的幾個技巧:

  • 進入Visual Mode后,通過3j,快速選擇四行
  • 注意使用x后,就會退出Visual Mode,需要用.命令來刪掉各列的空格
  • 使用gv命令,選擇之前選中的列block進行進一步操作
  • 使用yyp復制一行后,用Vr命令全部替換成-字符
批量修改各行文字內容

在使用Visual Mode選中一批內容后,可以進行批量修改,需要注意的是,使用諸如c/I/A命令對選中區域進行修改后(注意這里的插入不使用i/a,是因為i和a已經用于指示對text object的操作了,類似via),顯示的只是其中一行的變化,只有當你按下ESC回到Normal Mode的時候,其它行的修改才會呈現出來。

圖1-6 批量修改文字內容

II Command Mode

我們可以在Normal Mode下輸入":"來進入命令模式,在命令模式下,可以針對一個區域的內容進行增、改、刪、替換等等操作,下圖比較完整地總結了各項命令模式下可執行的操作:

圖2-1 命令模式下的各種操作
選取執行命令的范圍

對于以下文檔,通過各種選擇[range]的方式可以選取到我們想要的行:

<!DOCTYPE html>
<html>
<head><title>Practical Vim</title></head>
<body><h1>Practical Vim</h1></body>
</html>

:/<html>/+1,/</html>/-1p

<head><title>Practical Vim</title></head>
<body><h1>Practical Vim</h1></body>

這里選中的邏輯是,先匹配<html>然后行數加1,再匹配到</html>的行數減1,生成選中范圍,然后打印出來。

圖2-2 選擇range時的各種范圍參數
m和t命令

m是move的縮寫,t是copy的縮寫(或者用co),為什么不是c呢,?嗯..我也不知道,在normal mode下c命令是剪切、修改的命令。
這兩個命令還是挺簡單的,結合上文關于range的選擇,看兩個例子就明白了。

圖2-3 t命令

m命令和t命令用法一致,只不過t是復制,m是移動,就這點不同。

在Command Mode執行Normal Mode Command

在命令模式下,也可以執行正常模式下的命令,比如.命令,i命令,A命令等等,這樣可以方便的在一個范圍內執行一個統一的操作。比如注釋一批命令行,或者統一在行尾添加分號等。

圖2-4 在一行結尾添加;
圖2-5 使用V模式配合normal .

以上操作其實可以用一條很簡單的外部命令來解決:
:%normal A;

重復執行ex command

在normal mode下,我們使用.命令來重復執行上一條修改。而如果要重復執行上一條ex command,則需要使用@:命令。
當執行過一次@:命令后,我們可以使用@@來繼續重復執行。
例如對于上例,我們使用:%normal A;在所有行后添加了;之后,使用@:就可以在所有行后再添加一個;了。

補全ex command

在我們輸入ex command的時候,輸入命令的一部分后,使用<C-d>可以打印出所有可能的命令列表。

圖2-6 補全命令列表

使用<Tab>可以在可能的命令中進行輪詢,<S-Tab>進行反向輪詢(輪詢開始后用左右鍵也可以)

將當前光標下的內容插入到命令中

<C-r><C-w>這一映射組合可以將光標所在位置的內容插入到命令中去。我們也可以利用*****命令來實現快速的查詢匹配。
命令相當于執行/<<C-r><C-w>><CR>(這里< >是一個查詢邊界,在后面介紹查詢相關內容的時候會再具體介紹)
例子:使用
命令進行快速匹配,再配合cw和%s及<C-r><C-w>進行全文替換

圖2-7 匹配及替換

再使用:%s//<C-r><C-w>/g就可以把全文的tally替換為counter,這里的匹配中使用//即是使用現有的匹配。

翻找歷史命令

輸入:后,通過上/下鍵可以翻找之前執行過的命令。
Vim默認會保存最近20條執行過的命令,可以通過:set history命令來設定一共保存多少條。

Command-Line Window

在進入命令行模式的時候,我們沒法方便地編輯、組合多條命令。
這時候我們可以使用q:進入命令行窗口,就可以像編輯文本一樣來編輯命令行。如果要推出窗口模式,我們可以選擇使用:q(就像退出一個Vim窗口一樣),或者我們使用<CR>。要注意的是,如果我們用<CR>來退出命令模式,那當前光標所在行的命令會被執行到活動窗口的文本上。
在Command-Line模式下,我們也可以使用<C-f>來進入Command-Line Window。

圖2-8 進入命令行窗口的各種方式

在Shell中執行命令

我們在Vim中,如果想要執行Shell命令,可以很方便地通過:!{cmd}
來進行執行,如果直接執行:shell
命令,可以直接調用出一個shell窗口。
我們可以很方便的將Vim和Shell中的內容分別作為各自的標準輸入輸出來使用,總結見下表
補充圖片補充圖片補充圖片補充圖片補充圖片補充圖片補充圖片
例如我們可以通過:read !ls /很方便地將/目錄下的內容讀取并輸入到當前Vim文檔中。
或者,假設我們的光標停在print "hello world"這樣一個python語句上,使用:.write !python命令,可以直接調用python執行這段語句。

III Vim的buffer及標簽

批量打開文件的buffer

如果我們在命令行下,通過類似vim *.txt的命令打開了一批文件,Vim會把它們讀入buffer中去,我們可以通過:bnext :blast等命令依次查看buffer中的文件內容。用:ls就能查看當前的文件狀態。這個命令結果中,每個文件都被賦予了一個編號。我們可以通過:buffer N直接跳轉到這個文件。
如果我們不想繼續編輯了,那就可以通過:bdelete N1 N2 N3..來刪除不需要的buffer,也可以通過:N,M bdelete來連續地刪除。
Vim內建的操作buffer的工具不是很方便,如果我們想要批量管理文件、批量執行命令,可以通過使用tab list、分屏顯示、或者argument list來進行。

argument list

argument list組合一批文件,便于進行瀏覽,同時也可以批量對他們執行Ex命令。
執行*vim .txt之后,可以使用args來管理批量打開的文件。

圖3-1 用args批量管理文件

也可以先進入Vim后,使用args命令打開相應的文件。

$cd code/files/mvc
$vim
:args  index.html app.js
:args

這樣也可以達到同樣的效果。

也可以使用通配符打開文件:

圖3-2 args使用通配符打開文件

使用:args `cat .chapters`命令,還可以從chapters文件里讀取內容,作為args命令的輸入參數,加載到args list里。
這里我們要注意的是,arg list和buffer list并不是一回事,這其中應該是一個包含的關系,buffer list里是所有當前打開編輯的文件,就像我們整個工作桌面,args命令就像從中單獨拉出一批文件放置在一堆,便于我們查看和批量操作。
使用:next :prev來遍歷arg list中的文件。

hidden模式與argdo/bufdo批量操作

使用:h hidden我們可以看到hidden buffer的作用。
默認情況下,hidden是關閉的,意味著當一個buffer被舍棄時就被unload了;當hidden開啟的情況下,當一個buffer被舍棄,只是被隱藏起來。
當我們編輯了一個buffer后,沒有保存就使用:bnext進入下一個buffer時,Vim會提示我們需要確認是否保存修改,如果我們不想理會這一提示,可以使用:bnext!,這樣之前的buffer就進入了hidden狀態。
當我們退出Vim的時候,Vim會將hidden模式的buffer按次序調用到活動窗口,詢問我們是否需要保存修改。

圖3-3 關于hidden buffer的相關選項

在使用:argdo和:bufdo命令的時候,Vim執行的是如下的順序

:first
:{cmd}
:next
:{cmd}

這樣對于被修改過的前一個buffer,Vim就會不停詢問是否要處理修改結果。這時候我們就應該將hidden選項置為on,一口氣執行完命令,然后:wa保存所有的改變。

分隔窗口

Vim可以將窗口分隔成多個區塊,進行管理。分割后,多個分隔窗口共享同一個buffer,展示同一個文件。使用:e命令可以在新分隔的窗口中打開新的內容。使用:sp {file}也能達到同樣的效果。


圖3-4 分隔窗口
圖3-5 關閉窗口
圖3-6 重新規劃窗口大小
使用標簽瀏覽

當我們用:e在Vim中編輯一個文件,默認情況下Vim會把它加入到buffer,然后在當前的窗口中展示它。為了展示方便,我們也可以使用Tab標簽來管理文件。
要注意的是,Vim的標簽和其它一些文本編輯器的標簽不一樣。一般文本編輯器的一個標簽,就代表了一個打開的文件,但Vim的標簽和buffer內容并不是一個一對一的關系,Vim的每個標簽都可以看成一組窗口的集合,從下圖可以看出,每個標簽對應的窗口集合,深色是活動窗口。

圖3-7 Vim的標簽用于窗口集合的管理

下表中列出了標簽的相關管理命令,包括打開關閉和切換:


圖3-8 標簽打開及關閉
?圖3-9 標簽切換

如果要改變標簽的順序,我們可以使用:tabmove [N]這個Ex命令來實現。

IV Vim的文件打開及保存

在:edit中列出路徑

使用:edit {file}打開一個文件進行編輯,是Vim基本的編輯方式。
在打開保存中,有時候需要修改保存的路徑,這時候要是能快速查看到當前編輯文件的路徑會很方便。
按《Practical Vim》所說,用:edit %<Tab>這里的%表示了當前文件的絕對路徑,通過Tab就可以補全出完整內容。
但實際操作過程中,我在Vim中使用這個方法顯示的是相對路徑,經過查詢資料,使用:edit %:p<Tab>命令,實現了絕對路徑的效果。不知道是版本差異還是配置原因,用的時候注意一下就好。
一個更有用的技巧,是使用:h來去掉文件名,只列出絕對路徑的目錄位置。同樣的,與書中差異在于需要多加一個:p,即:edit %:p:h<Tab>。
當列出路徑后,再輸入文件首字母,就能繼續用Tab鍵補全文件名。

find命令

通過編輯path參數,我們可以使用find命令來查找文件。

:set path+=app/**

這條命令在path中添加了app/下的所有子目錄(**通配符),此時,我們可以使用:find Navigator.js在path路徑下尋找并打開該文件,如果不存在,會有報錯。
如果在命令行中,輸入:find Navigater.js<Tab>的話,Vim會將第一個找到的同名文件路徑補全出來,再按一次會顯示下一個匹配的路徑文件。

netrw

在Vim中輸入:E可以進入Vim自帶的explorer插件,可以在里面像資源管理器一樣瀏覽、修改目錄和文件,可以通過回車選中想要編輯的文件,直接在buffer中打開。
關于怎么在不選中任何文件的情況下退出,書中寫使用<C-^>,可是一直沒有嘗試成功。查了資料之后,發現可以使用:bd刪除當前buffer(也就是說explorer也是個buffer),回到上一個編輯界面。
P.S 這個問題用百度查了半天沒結果,google了一下馬上就知道了..也是一時腦殘就不該問百度技術問題。

如果要向一個不存在的目錄保存文件,需要配合shell命令創建文件夾:

:!mkdir -p %:p:h
:write
用超級用戶權限保存文件

在一個權限管理比較嚴格的環境,一般我們都使用非root權限進行登錄,當修改一些需要root權限才能修改的文件時,常常忘記切換用戶,改了半天才發現無法保存,這時候是不是只能關閉文件--切換用戶--重新打開編輯?
其實配合shell命令,我們可以直接在Vim中實現超級用戶權限保存文件的功能:

:w !sudo tee % > /dev/null

實現原理是,write命令把文件內容作為輸入提供到后續的shell命令,sudo tee %..這里的%就跟上面幾個例子中提到的一樣,代表當前編輯的文件(使用%:p應該也是一樣的),然后把stdout輸出到黑洞去。

V 文件內的快速跳轉

沒有什么需要特別說明的,都是一些熟能生巧的操作,記錄備查,在操作中多使用就好了:

圖5-1 g命令的作用
?圖5-2 基于單詞的跳轉

還有一個W命令,與w不同在于它是根據詞邊空格來判斷一段詞的結束,而w只是識別到一個單詞,在例如 it's/you're這樣的詞組情況下,就能體現出不同了。

圖5-3 f命令

f命令用于根據字母跳轉到響應位置,使用一次后,";"用于跳轉到下一個,而","跳轉到上一個。

d{motion}可以刪除到motion位置的內容:
dt. 刪除當前位置到第一個.的內容
d/ge<CR> 刪除當前位置到第一個"ge"位置的內容

使用vit等命令,選中某個區域中的內容,這種方法適用于各種普通命令,比如d/c/y等等。

圖5-4 vi/a{tag}

圖5-5 邊界文本對象

考慮到空格的情況,一般來說,d命令都適用于aw as。c命令適用于iw is。

mark與跳轉

使用m{a-zA-Z}命令可以設置mark點,然后可以通過'或者來進行跳轉,不同在于'跳轉到mark所在句子的第一個非空格位置,而直接跳轉到精確的位置上。
有一些mark位置是自動的:

圖5-6 自動mark標記位置

我們還可以用%立刻匹配到相對應的閉合符號處:

圖5-7 使用%跳轉到對應的閉合符號
圖5-8 跳轉操作實例
jump和change

:jumps可以看到跳轉的記錄
<C-o>/<C-i>可以用來向后/向前跳轉到相應的文件。

圖5-8 跳轉操作匯總

使用:changes可以查看到文檔中發生修改的記錄。
使用g;和g,可以在最近修改的位置之間發生跳轉。

VI 寄存器和宏命令

Vim的復制/剪切/粘貼操作都涉及到寄存器的概念。
最常用也最感受不到的寄存器,被稱為無名寄存器。我們不指明使用的寄存器的時候,使用的就是無名寄存器。
但是有時候只用無名寄存器會存在問題,比如下面這個例子:
y、d、p都用到無名寄存器,因此無法實現我們想要做到的目標。


圖6-1 無名寄存器的不足

這時候就需要使用到指名的寄存器,使用"可以指定寄存器來進行操作。其實對于Yank,它會有一個獨立的寄存器--"0寄存器。因此對于上面這個例子,可以這么操作:

圖6-2 "0寄存器

或者我們可以在刪除的時候使用黑洞寄存器("_寄存器),這樣就不會覆蓋無名寄存器。

圖6-3 黑洞寄存器
圖6-4 其他寄存器

在插入模式下,可以使用<C-r>{register}來paste寄存器中的內容。
插入模式的<C-r>0等同于"0p

宏命令

這里把宏命令和寄存器放在一起,因為宏也是存放在寄存器中然后調用執行的。
我們使用q{register}命令來開啟記錄模式,把后面的操作記錄到指定寄存器中。


圖6-5 宏命令的保存

使用:reg a,可以看到寄存器a中的內容。

圖6-6 通過寄存器調用宏

我們一樣可以通過@{register}調用寄存器中的一段宏,也可以用@@來重復上一次調用寄存器的操作。

.命令可以用于重復執行命令,但是不能為.命令指定次數,這時候就可以利用marcro

圖6-7 使用宏命令多次執行.命令

執行宏命令還可以分為串行和并行模式兩種情況。

考慮下面這樣一個宏命令

圖6-8 宏命令的串行和并行

如果我們要串行執行這個宏命令,直接使用N@a就可以了,就像這樣

圖6-9 串行執行宏命令

但是這種執行有一個缺點,如果在執行過程中,f.這個命令沒有找到內容,會產生內部錯誤導致命令執行中斷,下例中,執行到第三行的時候就出現了問題,導致后面的內容沒有被改變:

圖6-10 串行命令的中斷

這時候我們可以改變策略,使用并行執行的操作。方法是先一樣錄制宏命令后,通過可視模式選中要修改的行來進行并行宏命令操作。


圖6-11 并行執行命令

使用并行執行是一種更健壯的選擇,但是有時候我們需要在執行中得到可能發生的錯誤的提示,這時候,采用串行執行就是一個更好的選擇。所以具體用哪種方法,是要根據情況進行選擇的。

對一批文件執行宏命令

同樣也分為兩種模式:串行和并行。
大致思路是:
并行模式
通過:argdo normal @{register}來批量執行。
要注意的是,這個方式下,會對用來錄制腳本的第一個文件進行重復的修改,我們要在執行argdo之前,使用:edit!來把第一個文件的修改全部回退掉。然后進行批量執行。
串行模式
在宏命令中,顯式地錄制:next命令,跳轉到arglist中的下一個文件里去。再重復執行相應的命令。

注意在這里選擇那種模式執行,和單一文件內執行有所不同,由于不能一眼看到執行的結果和錯誤,如果選擇并行模式,對于出現報錯的情況,需要一一查看所有的文件來確認情況。而串行模式下,執行會停止在出錯的位置,方便我們判斷。如何取舍,要自己判斷。

下面是一段為代碼封裝一層module的宏,緊湊實用,記錄備查


?圖6-12 添加代碼封裝的宏
通過宏生成一個計數器
圖6-13 錄制宏生成計數器
圖6-14 利用宏給各行添加數字標記
修改宏的內容

使用qA命令,可以在a寄存器的現有內容之后繼續添加內容。這種方法只能追加,如果想要在宏的中間添加內容,就要用另一種辦法。
思路是這樣的:利用寄存器其實就是記錄一段命令操作內容的原理,將錄制好的寄存器內容黏貼出來進行修改,然后再拷貝回原來的寄存器里去。
拷貝的方式也有兩種,一種直接"{register}dd,整行剪切進去,這樣的做法一般沒有問題,但是末尾會有一個回車,有時候可能會導致宏的操作被干擾,更保險的做法是按字符來進行黏貼。

圖6-15 將黏貼出來的寄存器內容進行修改
圖6-16 使用"ay$按字符形式保存到寄存器

VII 模式、搜索與替換

magic search 和 very magic search

使用/來進行模式匹配時,默認是使用magic search。對于一些特殊的符號,如果要按照特殊意義來使用,則需要用\來轉義,比如()和{}。注意,我們只需要轉義前一個符號,后面那個會自動轉義。有時候這樣做太麻煩了,于是我們可以使用\v來進入very magic search,這類符號就不需要轉義操作了。具體區別,可以參看下表

圖7-1 magic search和very magic search

注意,在very magic模式下,Vim會將一切符號認作是特定符號,除了 _ 字母 數字以外。但上例中的#,我們是按普通內容匹配的,為什么在very magic下也沒有轉義還是按普通內容匹配了呢?
Vim的解釋是,所有暫時沒有被分配特殊意義的字符,會按照字面來匹配,#暫時不代表特殊意義,因此無需轉義。如果今后#被使用為特殊符號,那在very magic模式下匹配#就需要用#了。

verynomagic 模式

.,*,?這些特殊的正則符號,在進行正則匹配時不可或缺十分好用,但是如果我們想在pattern匹配中匹配這些符號的字面意思,就需要轉義進行,當符號多的時候就很麻煩。這時候我們應該使用\V進入verynomagic或者叫Verbatim搜索模式,此時這些特殊正則符號無需轉義,就按照字面來匹配。

圖7-2 Verbatim search
使用()來捕獲次級匹配

我們來看一下這么一個匹配
/\v<(\w+)_s+\1>
這里的<>用來匹配邊界,_s+代表匹配空白符或回車,重點是這里的\1,匹配的是前面用()包圍的部分\w+,對于要反復使用匹配模式,特別是匹配模式比較復雜的情況下,用這種次級匹配就會比較方便。

對于用<>來確定單詞邊界,也是一個很有用的技巧:
對于下面的內容如果我們要找出the這個單詞,使用/the來匹配,會發現匹配到許多不需要的東西,因為they、their中的the也被匹配到了。
the problem with these new recruits is that
they don't keep their boots clean.
這時候如果用/\v<the>來匹配,就能正確地獲得the這個單詞了。

有時候我們使用()來進行分組,比如下面這個匹配:
/\v(And|D)rew Neil
我們使用()來匹配Andrew或者Drew,但是我們對于是And和D這兩個模式并不關心,也不會用到,如果就這么占用了一個\1寄存器,會很浪費。這時候我們可以用%來讓Vim不要記錄這個模式
/\v%(And|D)rew Neil

下面這個例子可以將匹配到的內容再進行對調:
/\v(%(And|D)rew) (Neil)
:%s//\2, \1/g

圖7-3 常用的atom
調整匹配的邊界

有時候我們匹配一段內容,但是實際想要修改或操作的只是其中一部分,這時候我們可以用\zs和\ze來調整匹配的邊界,下例中,將兩邊的"排除在外了

圖7-4 調整邊界
一個綜合的搜索例子

對于下面這個文本,我們想要搜索[]內的內容,應該怎么做呢。

Search items: [http://vimdoc.net/search?q=/\\]

首先,我們可以將[]內的內容復制下來。使用i[的技巧,將光標放置到[]之內的任何地方,然后"uyi[,就可以將[]中的內容復制到u寄存器。然后進入匹配模式,輸入
/\V<C-r>u就能發現,從u寄存器中將內容讀取到了匹配模式中。
生成的查詢語句如下:

/\Vhttp://vimdoc.net/search?q=/\\

但這個時候會發現,實際只匹配到了http:這個內容,那是因為第二個/就把這個匹配過程終結了。

這時候,我們有幾種選擇,一種是將所有的/轉義:
**/\Vhttp://vimdoc.net/search?q=/\\\\ **

如果這樣覺得太麻煩,可以選擇用?進行反向搜索,這時候第二個?成了搜索終結的符號,只需要將?轉義就可以了:
?http ://vimdoc.net/search?q=/\\\\

注意,無論哪種搜索模式,\都是需要被轉義的

統計匹配次數的小技巧

利用替換命令,可以有個小技巧用于統計一個匹配模式的出現次數。
當我們先獲得了一個匹配后,可以用如下命令
:%s///gn
來獲取模式的匹配次數。這條命令如果沒有最后的n,會把所有的之前的匹配模式替換為空字符,最后的n會抑制住這個替換操作,Vim下放會顯示這個模式一共有多少個,出現在多少行中。

搜索后光標位置

使用/e在搜索后將光標停止在搜索的尾部:


圖7-5 搜索后將光標置于搜索的尾部
一步步完善搜索

對于下面文檔,如果我們想把用于引用的'替換為",應該怎么做呢?

This string contains a 'quoted' word.
This string contains 'two' quoted 'words.'
This 'string doesn't make things easy.'

首先,應該想辦法把這個模式匹配出來:
/\v'.+'

結果發現,匹配成了這樣:

This string contains a 'quoted' word.
This string contains 'two' quoted 'words.'
This 'string doesn't make things easy.'

優化一下:
/\v'[ ^']+'

This string contains a 'quoted' word.
This string contains 'two' quoted 'words.'
This 'string doesn't make things easy.'

再優化一下:
/\v'([ ^']|'\w)+'
至此,匹配成功。

為了替換方便,我們可以再改造一下,變成
/\v'(([ ^']|'\w)+)'
然后使用
:%s//"\1"/g
就可以達成我們的目標了。注意這里的一個技巧,%s//就是使用上一次的匹配模式結果,避免我們重復進行輸入。

匹配和替換過程中,特別是復雜的模式,不要想著一次寫成,可以多嘗試幾次,慢慢完善。

圖7-6 替換中常用的一些字符
flags

最常用的替換,我們應該很熟悉:
:%s/pattern/sub/g
這里的%指的是全文每行都要進行替換,而結尾的g flag,則指要將一行內所有匹配到的都替換。除了g之外,還有很多的flag,比如之前我們用過的n flag取消實際的替換操作,還有c flag,在每次替換前都詢問我們是否要進行替換操作。

圖7-7 針對c flag的應答
重復替換操作

加入我們執行了下面這個替換:
:s/target/replacement/g
我們馬上意識到了問題,沒有%,這個操作就只在當前行生效。這時候,我們不需要修改命令,增加%來執行替換,只要簡單地使用g&命令,就可以達到同樣的效果。
g&命令,相當于:%s//~/&,// ~ /&分別表示使用上一次的匹配模式,替換成上一次進行替換用的內容,同時使用上一次的flag。
這個命令也可以用:%&&來代替,第一個&表示復用上一次的替換,后一個&表示復用上一次的flag。下面有個例子,在使用可視模式替換時,:&&很好用。

圖7-8 第一次替換把所有內容都改了
圖7-9 撤回后利用gv和&&快速執行替換
從寄存器獲取替換內容

有兩種方式,一種是通過值傳遞
:%s//<C-r>0/g 直接將0 寄存器的內容讀取到命令中,好處是直接能看到,缺點在于如果其中有些特殊字符,可能需要修改轉義。
還有一種,通過引用傳遞
:%s//=@0/g
0寄存器保存著上一次復制的內容。

替換時進行數學運算

假設對于以下這樣的文本,我們想要把每一級的標題級別,都提升一級

<h2>Heading number 1</h2>
<h3>Number 2 heading</h3>
<h4>Another heading</h4>

使用這個匹配:
/v</?h\zs\d就能直接匹配到<>內的數字,這里又用到了\zs的技巧。
替換命令:
:%s//=submatch(0)-1/g
這里的submatch(0)就是代表了上一次的匹配內容。

交換兩個詞

如果要將兩個詞做對調,使用普通的替換操作是做不到的,一種方法是用字典來實現:
假設我們要替換dog和man這兩個詞。
先讓我們看看字典是怎么一回事:


圖7-10 字典
圖7-11 具體實現
在多個文件中替換

最簡單粗暴的辦法,就是將可能需要替換的文件全部讀入arglist,然后一次性argdo
匹配模式 /Pragmatic\ze Vim
讀入文件 :args **/.txt*
批量執行 :argsdo %s//Practical/g
忽略錯誤批量執行 :argsdo %s//Practical/ge

global命令

大名鼎鼎的:g命令,格式如下:

:[range] global[!] /{pattern}/ [cmd]

原理是,在range范圍內,根據pattern標記出所在的行,針對這些行,執行cmd命令的內容。相對應的有:v命令,是對除了pattern之外的行進行標記,并執行cmd命令。

例如
:g/good/d
這個命令,就會將所有含有good的行刪除掉。

sort內容

對于類似下面這樣的文件內容,如果我們要對{}中的各行根據首字母排序

html {margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
line-height: 1.5;
color: black;
background: white;

}
body {
line-height: 1.5;
color: black;
background: white;
}

:g/{/ .+1,/}/-1 sort 這么一條命令就夠了。

相應的,如果要對一些行進行縮進操作:
:g/{/sil .+1,/}/-1 > 就可以實現
這里的/sil用于屏蔽命令的返回。

只要充分掌握好pattern匹配和各類命令,:g可以實現許多強大的功能。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容