最近集中讀了幾本關于Vim的書,?以《Practical Vim》最為詳盡,特重新溫習并總結成篇,一來作為復習和練習,二來方便今后備查。
由于主要給自己看,一些特別基本的操作就不在贅述,了解normal mode和insert mode的區別和基本操作,能夠進行基本的增刪改操作即可。
本文示例摘抄翻譯自Drew Neail的《Practical Vim》一書
<C-v>代表Ctrl+v,這里的例子主要適用于linux環境下的vim,對于windows環境,存在快捷命令沖突的情況,需要注意。
主要內容:
- Vim的Visual Mode和Command-Line Mode
- 文件中、文件之間快速跳轉的技巧
- Vim的寄存器、宏命令
- Vim的模式匹配、搜索與替換--特別是神奇的global命令
I Visual Mode
可視模式,用于選擇“一塊”內容,并進行相應操作。
命令 | 作用 |
---|---|
v | 基于單詞的可視模式 |
V | 基于行的可視模式 |
<C-v> | 基于塊的可視模式 |
gv | 重新選中上次選中的塊 |
o命令
在進入可是模式后,相關的移動操作依然可用,在移動的同時會選中經過的內容,這里有一個比較有用的移動命令是o,用于跳轉到本次選中區域的另一端——就不用取消重選了。
用“.”來重復命令
當我們使用.命令來重復之前的執行時,如果是針對一個visual block的執行,重復執行也是再次針對這個block的。
當設置過 :set shiftwidth=4 softtabstop=4 expandtab
這條命令之后,就可以使用>和<命令來進行縮進和取消縮進的操作了。這時候,通過v命令選中后,執行>命令縮進。此時如果要再縮進一級,直接使用.命令進行重復就可以了。
vitU和gUit
有時候我們需要修改一段html代碼標簽內的東西,強大的Vim給了我們一個直接選中標簽內內容的命令vit,這個命令是v和it命令的組合,其中it命令是一個特殊的motion command。選中后可以執行各類操作命令。比如如下例子:
這樣的操作有一個缺點,由于vit和U命令是分開的,如果我們使用.命令,它并不是完整的重復vit和U命令,而是針對同一行同樣的選中位置部分執行U命令,在上例中,通過j.重復后,在第三行,標簽內的內容會變成THRee而不是我們希望的THREE。為了解決這個問題,我們需要將命令在一條內執行完成。
Vim也提供了相應的方案,使用gUit命令就可以,其中gU是一個操作命令,it是確定命令的執行范圍。
使用<C-v>來進行列操作
還記得沒用Vim之前,第一次進行列操作用的是UltraEdit,當時覺得好酷炫,鼠標拖一下就可以方便的在一列上進行修改;如今連鼠標都不用,按幾下鍵盤就實現了相同的功能,豈不是更爽!
這里面用到的幾個技巧:
- 進入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的時候,其它行的修改才會呈現出來。
II Command Mode
我們可以在Normal Mode下輸入":"來進入命令模式,在命令模式下,可以針對一個區域的內容進行增、改、刪、替換等等操作,下圖比較完整地總結了各項命令模式下可執行的操作:
選取執行命令的范圍
對于以下文檔,通過各種選擇[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,生成選中范圍,然后打印出來。
m和t命令
m是move的縮寫,t是copy的縮寫(或者用co),為什么不是c呢,?嗯..我也不知道,在normal mode下c命令是剪切、修改的命令。
這兩個命令還是挺簡單的,結合上文關于range的選擇,看兩個例子就明白了。
m命令和t命令用法一致,只不過t是復制,m是移動,就這點不同。
在Command Mode執行Normal Mode Command
在命令模式下,也可以執行正常模式下的命令,比如.命令,i命令,A命令等等,這樣可以方便的在一個范圍內執行一個統一的操作。比如注釋一批命令行,或者統一在行尾添加分號等。
以上操作其實可以用一條很簡單的外部命令來解決:
:%normal A;
重復執行ex command
在normal mode下,我們使用.命令來重復執行上一條修改。而如果要重復執行上一條ex command,則需要使用@:命令。
當執行過一次@:命令后,我們可以使用@@來繼續重復執行。
例如對于上例,我們使用:%normal A;在所有行后添加了;之后,使用@:就可以在所有行后再添加一個;了。
補全ex command
在我們輸入ex command的時候,輸入命令的一部分后,使用<C-d>可以打印出所有可能的命令列表。
使用<Tab>可以在可能的命令中進行輪詢,<S-Tab>進行反向輪詢(輪詢開始后用左右鍵也可以)
將當前光標下的內容插入到命令中
<C-r><C-w>這一映射組合可以將光標所在位置的內容插入到命令中去。我們也可以利用*****命令來實現快速的查詢匹配。
命令相當于執行/<<C-r><C-w>><CR>(這里< >是一個查詢邊界,在后面介紹查詢相關內容的時候會再具體介紹)
例子:使用命令進行快速匹配,再配合cw和%s及<C-r><C-w>進行全文替換
再使用:%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。
在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來管理批量打開的文件。
也可以先進入Vim后,使用args命令打開相應的文件。
$cd code/files/mvc
$vim
:args index.html app.js
: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按次序調用到活動窗口,詢問我們是否需要保存修改。
在使用:argdo和:bufdo命令的時候,Vim執行的是如下的順序
:first
:{cmd}
:next
:{cmd}
這樣對于被修改過的前一個buffer,Vim就會不停詢問是否要處理修改結果。這時候我們就應該將hidden選項置為on,一口氣執行完命令,然后:wa保存所有的改變。
分隔窗口
Vim可以將窗口分隔成多個區塊,進行管理。分割后,多個分隔窗口共享同一個buffer,展示同一個文件。使用:e命令可以在新分隔的窗口中打開新的內容。使用:sp {file}也能達到同樣的效果。
使用標簽瀏覽
當我們用:e在Vim中編輯一個文件,默認情況下Vim會把它加入到buffer,然后在當前的窗口中展示它。為了展示方便,我們也可以使用Tab標簽來管理文件。
要注意的是,Vim的標簽和其它一些文本編輯器的標簽不一樣。一般文本編輯器的一個標簽,就代表了一個打開的文件,但Vim的標簽和buffer內容并不是一個一對一的關系,Vim的每個標簽都可以看成一組窗口的集合,從下圖可以看出,每個標簽對應的窗口集合,深色是活動窗口。
下表中列出了標簽的相關管理命令,包括打開關閉和切換:
如果要改變標簽的順序,我們可以使用: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 文件內的快速跳轉
沒有什么需要特別說明的,都是一些熟能生巧的操作,記錄備查,在操作中多使用就好了:
還有一個W命令,與w不同在于它是根據詞邊空格來判斷一段詞的結束,而w只是識別到一個單詞,在例如 it's/you're這樣的詞組情況下,就能體現出不同了。
f命令用于根據字母跳轉到響應位置,使用一次后,";"用于跳轉到下一個,而","跳轉到上一個。
d{motion}可以刪除到motion位置的內容:
dt. 刪除當前位置到第一個.的內容
d/ge<CR> 刪除當前位置到第一個"ge"位置的內容
使用vit
等命令,選中某個區域中的內容,這種方法適用于各種普通命令,比如d/c/y等等。
考慮到空格的情況,一般來說,d命令都適用于aw as。c命令適用于iw is。
mark與跳轉
使用m{a-zA-Z}命令可以設置mark點,然后可以通過'或者來進行跳轉,不同在于'跳轉到mark所在句子的第一個非空格位置,而
直接跳轉到精確的位置上。
有一些mark位置是自動的:
我們還可以用%立刻匹配到相對應的閉合符號處:
jump和change
:jumps可以看到跳轉的記錄
<C-o>/<C-i>可以用來向后/向前跳轉到相應的文件。
使用:changes可以查看到文檔中發生修改的記錄。
使用g;和g,可以在最近修改的位置之間發生跳轉。
VI 寄存器和宏命令
Vim的復制/剪切/粘貼操作都涉及到寄存器的概念。
最常用也最感受不到的寄存器,被稱為無名寄存器。我們不指明使用的寄存器的時候,使用的就是無名寄存器。
但是有時候只用無名寄存器會存在問題,比如下面這個例子:
y、d、p都用到無名寄存器,因此無法實現我們想要做到的目標。
這時候就需要使用到指名的寄存器,使用"可以指定寄存器來進行操作。其實對于Yank,它會有一個獨立的寄存器--"0寄存器。因此對于上面這個例子,可以這么操作:
或者我們可以在刪除的時候使用黑洞寄存器("_寄存器),這樣就不會覆蓋無名寄存器。
在插入模式下,可以使用<C-r>{register}來paste寄存器中的內容。
插入模式的<C-r>0等同于"0p
宏命令
這里把宏命令和寄存器放在一起,因為宏也是存放在寄存器中然后調用執行的。
我們使用q{register}命令來開啟記錄模式,把后面的操作記錄到指定寄存器中。
使用:reg a,可以看到寄存器a中的內容。
我們一樣可以通過@{register}調用寄存器中的一段宏,也可以用@@來重復上一次調用寄存器的操作。
.命令可以用于重復執行命令,但是不能為.命令指定次數,這時候就可以利用marcro
執行宏命令還可以分為串行和并行模式兩種情況。
考慮下面這樣一個宏命令
如果我們要串行執行這個宏命令,直接使用N@a就可以了,就像這樣
但是這種執行有一個缺點,如果在執行過程中,f.這個命令沒有找到內容,會產生內部錯誤導致命令執行中斷,下例中,執行到第三行的時候就出現了問題,導致后面的內容沒有被改變:
這時候我們可以改變策略,使用并行執行的操作。方法是先一樣錄制宏命令后,通過可視模式選中要修改的行來進行并行宏命令操作。
使用并行執行是一種更健壯的選擇,但是有時候我們需要在執行中得到可能發生的錯誤的提示,這時候,采用串行執行就是一個更好的選擇。所以具體用哪種方法,是要根據情況進行選擇的。
對一批文件執行宏命令
同樣也分為兩種模式:串行和并行。
大致思路是:
并行模式
通過:argdo normal @{register}來批量執行。
要注意的是,這個方式下,會對用來錄制腳本的第一個文件進行重復的修改,我們要在執行argdo之前,使用:edit!來把第一個文件的修改全部回退掉。然后進行批量執行。
串行模式
在宏命令中,顯式地錄制:next命令,跳轉到arglist中的下一個文件里去。再重復執行相應的命令。
注意在這里選擇那種模式執行,和單一文件內執行有所不同,由于不能一眼看到執行的結果和錯誤,如果選擇并行模式,對于出現報錯的情況,需要一一查看所有的文件來確認情況。而串行模式下,執行會停止在出錯的位置,方便我們判斷。如何取舍,要自己判斷。
下面是一段為代碼封裝一層module的宏,緊湊實用,記錄備查
通過宏生成一個計數器
修改宏的內容
使用qA命令,可以在a寄存器的現有內容之后繼續添加內容。這種方法只能追加,如果想要在宏的中間添加內容,就要用另一種辦法。
思路是這樣的:利用寄存器其實就是記錄一段命令操作內容的原理,將錄制好的寄存器內容黏貼出來進行修改,然后再拷貝回原來的寄存器里去。
拷貝的方式也有兩種,一種直接"{register}dd,整行剪切進去,這樣的做法一般沒有問題,但是末尾會有一個回車,有時候可能會導致宏的操作被干擾,更保險的做法是按字符來進行黏貼。
VII 模式、搜索與替換
magic search 和 very magic search
使用/來進行模式匹配時,默認是使用magic search。對于一些特殊的符號,如果要按照特殊意義來使用,則需要用\來轉義,比如()和{}。注意,我們只需要轉義前一個符號,后面那個會自動轉義
。有時候這樣做太麻煩了,于是我們可以使用\v來進入very magic search,這類符號就不需要轉義操作了。具體區別,可以參看下表
注意,在very magic模式下,Vim會將一切符號認作是特定符號,除了 _ 字母 數字以外。但上例中的#,我們是按普通內容匹配的,為什么在very magic下也沒有轉義還是按普通內容匹配了呢?
Vim的解釋是,所有暫時沒有被分配特殊意義的字符,會按照字面來匹配,#暫時不代表特殊意義,因此無需轉義。如果今后#被使用為特殊符號,那在very magic模式下匹配#就需要用#了。
verynomagic 模式
.,*,?這些特殊的正則符號,在進行正則匹配時不可或缺十分好用,但是如果我們想在pattern匹配中匹配這些符號的字面意思,就需要轉義進行,當符號多的時候就很麻煩。這時候我們應該使用\V進入verynomagic或者叫Verbatim搜索模式,此時這些特殊正則符號無需轉義,就按照字面來匹配。
使用()來捕獲次級匹配
我們來看一下這么一個匹配
/\v<(\w+)_s+\1>
這里的<>用來匹配邊界,_s+代表匹配空白符或回車,重點是這里的\1,匹配的是前面用()包圍的部分\w+,對于要反復使用匹配模式,特別是匹配模式比較復雜的情況下,用這種次級匹配就會比較方便。
對于用<>來確定單詞邊界,也是一個很有用的技巧:
對于下面的內容如果我們要找出the這個單詞,使用/the來匹配,會發現匹配到許多不需要的東西,因為they、their中的the也被匹配到了。
the
problem with the
se new recruits is that
the
y don't keep the
ir 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
調整匹配的邊界
有時候我們匹配一段內容,但是實際想要修改或操作的只是其中一部分,這時候我們可以用\zs和\ze來調整匹配的邊界,下例中,將兩邊的"排除在外了
一個綜合的搜索例子
對于下面這個文本,我們想要搜索[]內的內容,應該怎么做呢。
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在搜索后將光標停止在搜索的尾部:
一步步完善搜索
對于下面文檔,如果我們想把用于引用的'替換為",應該怎么做呢?
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//就是使用上一次的匹配模式結果,避免我們重復進行輸入。
匹配和替換過程中,特別是復雜的模式,不要想著一次寫成,可以多嘗試幾次,慢慢完善。
flags
最常用的替換,我們應該很熟悉:
:%s/pattern/sub/g
這里的%指的是全文每行都要進行替換,而結尾的g flag,則指要將一行內所有匹配到的都替換。除了g之外,還有很多的flag,比如之前我們用過的n flag取消實際的替換操作,還有c flag,在每次替換前都詢問我們是否要進行替換操作。
重復替換操作
加入我們執行了下面這個替換:
:s/target/replacement/g
我們馬上意識到了問題,沒有%,這個操作就只在當前行生效。這時候,我們不需要修改命令,增加%來執行替換,只要簡單地使用g&命令,就可以達到同樣的效果。
g&命令,相當于:%s//~/&,// ~ /&分別表示使用上一次的匹配模式,替換成上一次進行替換用的內容,同時使用上一次的flag。
這個命令也可以用:%&&來代替,第一個&表示復用上一次的替換,后一個&表示復用上一次的flag。下面有個例子,在使用可視模式替換時,:&&很好用。
從寄存器獲取替換內容
有兩種方式,一種是通過值傳遞
:%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這兩個詞。
先讓我們看看字典是怎么一回事:
在多個文件中替換
最簡單粗暴的辦法,就是將可能需要替換的文件全部讀入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可以實現許多強大的功能。