Vim實用技巧:global命令

global是什麼意思

:global命令結合了Ex命令與Vim的模式匹配這兩方面能力。憑藉該命令,可以在某個指定模式的所有匹配行上執行Ex命令。就處理重複工作的效率而言,global 命令是除點正規化以及宏之外,最為強大的Vim工具之一。

技巧98 認識global命令

:global 命令允許在某個指定模式的所有匹配行上執行Ex命令。首先研究一下它的語法。

:global命令通常採用以下形式(參見 :h :g

Vim實用技巧:global命令

)。

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

首先,在預設情況下,:global命令的作用範圍是整個檔案(%),這一點與其他大多數Ex命令(包括:delete、:substitute 以及 :normal)有所不同,這些命令的預設範圍僅為當前行(。)。

其次,{pattern} 域與查詢歷史相互關聯。這意味著如果將該域留空的話,Vim會自動使用當前的查詢模式。

另外,[cmd]可以是除 :global命令之外的任何Ex命令。在實際應用中,如表5-1中所列的那些Ex命令,無一不在處理文字過程中起到了極大的作用。順便提一下,如果不指定任何 [cmd],Vim將預設使用 :print。

還有,可以用 :global! 或者 :vglobal(v表示invert)反轉:global命令的行為。這兩條命令將指示Vim在沒有匹配到指定模式的行上執行 [cmd]。在下一節中,將會分別看到 :global 與 :vglobal的應用例項。

最後需要指出的是 :global命令在指定 [range] 內的文字行上執行時通常分為兩輪。第一輪,Vim在所有[pattern]的匹配行上做上標記。第二輪,再在所有已標記的文字行上執行 [cmd]。另外,由於 [cmd] 的範圍可單獨設定,因此可在多行文字段內進行操作,將在技巧101中講解這項強大的技術。

技巧99 刪除所有包含模式的文字行

將 :global 命令與 :delete命令一起組合使用,可以快速裁剪檔案內容。對於那些匹配 {pattern} 的文字行,既可以選擇保留,也可以將其丟棄。

以下內容取自Vimcasts。org歸檔網頁中有關前幾部主題的連結。

global/episodes.html

  1. Show invisibles
  2. Tabs and Spaces
  3. Whitespace preferences and filetypes

顯而易見,所有列表項均由兩部分資料構成:主題的標題及其URL。接下來,將利用 :global命令分別取出這兩組資料。

用 ‘:g/re/d’ 刪除所有的匹配

如果只想保留 標籤內的標題,而把其他行刪掉,該怎麼做呢?在本例中,由於每組連結的內容各佔一行,而其他文字行只包含或開或閉這兩種型別的標籤,因此,如果設計一個可以匹配HTML標籤的模式,再用它呼叫 :global命令,就可以刪掉所有該模式的匹配行了。

以下命令可以做到這一點。

➾ /\v\<\/?\w+>➾ :g//d

如果在Vimcasts。org的歸檔檔案中執行這兩條命令,檔案的內容將會變為:

Show invisiblesTabs and SpacesWhitespace preferences and filetypes

與 :substitute命令類似,也可以將:global命令的查詢域留空。這樣一來,Vim將會重用最後一次的查詢模式(參見技巧91)。這意味著在構造正則表示式的過程中,可以先進行粗粒度匹配,然後再對其進行精細調整,正如技巧85展示的那樣。

本例的正則表示式採用的是 very magic模式(在技巧74中有所涉及)。首先,它會匹配左尖括號(\<);然後,匹配可選的正斜槓(\/?);接下來,再匹配一個或多個單詞型字元(\w+);最後匹配表示單詞結尾的分隔符(>)。儘管這個正則表示式並不能匹配所有的標籤,但對於這個特定的例子來說,已經夠用了。

Grep一詞的來歷

請仔細琢磨一下 :global命令的簡寫形式:

➾ :g/re/p

re表示regular expression,而 p是 :print的縮寫,它作為預設的 [cmd]使用。如果我們把符號 / 忽略掉,便會發現單詞“grep”已然呼之欲出了。

用 ‘:v/re/d’ 只保留匹配行

這一次,我們將進行相反的操作。正如我們前面提到的,:vglobal或簡寫的 :v命令恰好與 :g命令的操作相反。也就是說,它用於在指定模式的非匹配行上執行Ex命令。

在本例中,包含URL的文字行很容易識別,它們都含有 href屬性。因此,執行以下命令,可以得到這些文字行。

➾ :v/href/d

以上命令可以解讀為“刪除所有不包含 href的文字行”。最終的結果如下。

僅僅憑藉一條命令,整篇文件就被精煉為我們感興趣的文字段了。

技巧100 將TODO項收集至暫存器

透過把 :global和 :yank 這兩條命令結合在一起,可以把所有匹配 {pattern}的文字行收集到某個暫存器中。

下列程式碼包含了幾行以“TODO”開頭的註釋行。

global/markdown.js

Markdown。dialects。Gruber = { lists: function() { // TODO: Cache this regexp for certain depths。 function regex_for_depth(depth) { /* implementation */ } }, “`”: function inlineCode( text ) { var m = text。match( /(`+)(([\s\S]*?)\1)/ ); if ( m && m[2] ) return [ m[1]。length + m[2]。length ]; else { // TODO: No matching end code found - warn! return [ 1, “`” ]; } }}

假設想把所有TODO項收集到一起。只需輸入以下命令,這些資訊就會變得一覽無餘。

➾ :g/TODO《 // TODO: Cache this regexp for certain depths。 // TODO: No matching end code found - warn!

請牢記,:print是 :global命令的預設 [cmd],它只是簡單地回顯所有匹配單詞“TODO”的文字行。這並沒什麼用處,因為一旦執行了其他命令,這些資訊將會消失。

這裡介紹另外一種做法。先將所有包含單詞“TODO”的文字行復制到某個暫存器,再把暫存器的內容貼上到其他檔案中,以備不時之需。

這一次,將用到暫存器a。首先執行 qaq,將其清空。對這個命令進行分解。qa會讓vim開始錄製宏,並把它存到暫存器a中,最後的 q則負責終止錄製。由於在錄製宏的過程中,我們沒有敲擊任何按鍵,因此暫存器最終被清空了。可以用下面的命令印證一下。

➾ :reg a《——- Registers ——- “a

現在,可以把包含TODO註釋的行復制到此暫存器中了。

➾ :g/TODO/yank A➾ :reg a《”a // TODO: Cache this regexp for certain depths。 // TODO: No matching end code found - warn!

此處有一個竅門,即要用大寫字母A引用暫存器。這意味著Vim將把內容附加到指定的暫存器,用小寫字母 a的話,則會覆蓋原有暫存器的內容。因此,這條global命令可以被解讀為“將所有匹配模式 /TODO/ 的文字行依次附加到暫存器 a。”

這一次,當再次執行 :reg a時,會發現暫存器 a已經存有兩組源自文件的TODO項了。(為了方便閱讀,已將這些內容調整為兩行,但在Vim中,換行符實際會顯示為 ^J。)此後,只需在任意分割視窗中開啟一個新緩衝區,再執行 “ap命令,就可以將暫存器 a的內容貼上進去了。

結論

本例只收集了兩個TODO項,即使手動操作也可以很快地完成。但是,以上介紹的技術具有很好的擴充套件性。如果某篇文件包含十幾個TODO項,採用該技巧將使我們事半功倍。

甚至可以將 :global命令與 :bufdo或 :argdo一起搭配使用,從一組檔案中收集所有的TODO項。這個任務就留給你作為練習吧,可以參考技巧36中類似的工作流程。

還有另外一種方案:

➾ :g/TODO/t$

這裡用到的 :t命令已經在技巧29中介紹。該命令是將所有TODO項複製到當前檔案的末尾,而不是把它們附加到暫存器。一旦執行完該命令,就可以在檔案的末尾看到這些TODO項了。由於此法不會影響暫存器的內容,因此相對簡單直接,但它在與 :argdo以及:bufdo命令一起使用時不太乾淨利落。

技巧101 將CSS檔案中所有規則的屬性按照字母排序

當Ex命令與 :global一起組合使用時,也可以為[cmd]單獨指定範圍。Vim允許以 :g/{pattern} 為參考點,動態地設定範圍。接下來,看看如何利用這一點,將CSS檔案中每一條規則的所有屬性均按照字母順序排列。

用以下 CSS 檔案作為演示。

global/unsorted.css

Line 1 html { - margin: 0; - padding: 0; - border: 0; 5 font-size: 100%; - font: inherit; - vertical-align: baseline; - } - body { 10 line-height: 1。5; - color: black; - background: white; - }

假設想把每一組規則內的屬性都按照字母順序排序。藉助Vim的內建命令 :sort(參見:h :sort ),就可以實現這一功能。

對單條規則的屬性進行排序

先用 :sort命令在該檔案的子集上練練手(參見表15-1)。

首先,使用文字物件 vi{,可以輕易地選中一段由 {} 所圍的文字塊。然後,執行 :‘<,’>sort,便可以將這些文字行按照字母順序重新排列了。如果每次僅對一條規則進行排序,此法完全可以勝任,但假設我們遇到的是一個包含數百條規則的樣式表呢?如果能把這一過程自動化豈不更好麼?

表15-1對檔案的子集進行排序

按鍵操作緩衝區內容

{start}html

{

margin: 0;

padding: 0;

border: 0;

font-size: 100%;

font: inherit;

vertical-align: baseline;

}

vi{

html {

 margin: 0;

 padding: 0;

 border: 0;

 font-size: 100%;

font: inherit;

 vertical-align: baseline;

}:’<,’>sorthtml {

b

order: 0;

font-size: 100%;

font: inherit;

margin: 0;

padding: 0;

vertical-align: baseline;

}

對所有規則的屬性進行排序

其實,可以用一條 :global 命令對檔案中所有規則的屬性進行排序。假設在本例的樣式表中執行以下命令。

➾ :g/{/ 。+1,/}/-1 sort

最終會得到以下結果。

html { border: 0; font-size: 100%; font: inherit; margin: 0; padding: 0; vertical-align: baseline;}body { background: white; color: black; line-height: 1。5;}

這條排序命令會在每條規則的{} 塊內執行。儘管本例中的樣式表僅僅包含十幾行文字,但對於內容更多的CSS檔案,此法也同樣適用。

這條命令很複雜,但掌握其機理後,將會由衷地讚歎 :global命令的強大。:global命令的標準格式如下。

:g/{pattern}/[cmd]

請牢記,Ex命令通常都會接受“範圍”作為其引數(正如技巧28討論的那樣)。對於:global命令內部的 [cmd],該規則依然有效。因此,可以將命令的模板擴充套件成以下形式。

:g/{pattern}/[range][cmd]

實際上,可以用 :g/{pattern} 匹配作為參考點,動態設定 [cmd]的 [range]。。 符號通常表示游標所在行,但在 :global命令的上下文中,它則表示 {pattern} 的匹配行。

可以把原有的命令拆分成兩條單獨的Ex命令進行講解,先分析命令的後半部分。以下是一條有效的Ex命令。

➾ :。+1,/}/-1 sort

如果去掉範圍中的偏移,該範圍可簡化為 。,/}/,其含義是“從當前行開始,一直到匹配模式 /}/ 的那一行為止”。偏移值 +1 與 —1 僅僅用於縮小操作範圍,讓我們把目光集中在 {} 之間的內容上面。對於排序前的原始CSS檔案,如果把游標置於第1行或第9行,以上這條Ex命令將會對相應 {} 之內的規則按照字母順序重新排序。

也就是說,只需將游標置於每個{} 塊的起始位置,再執行 :。,/}/ sort 命令,即可將其中的規則按照字母順序重新排序了。明白了麼?現在,試著用 :global命令中的 {pattern} 執行一次查詢。

➾ /{/

以上命令會將游標置於某個 {} 塊的起始位置,即我們的目標所在。現在,再重新將 :global 與 Ex命令 [cmd]組合在一起。

➾ :g/{/ 。+1,/}/—1 sort

其中,模式 { 會匹配每個 {} 塊的起始行。而對於每個匹配行,:sort會在匹配行到 {} 塊的結尾這個[range] 範圍內執行。最終,每一條規則的CSS屬性都會按照字母順序排列整齊。

結論

:global命令的廣義形式如下。

:g/{start}/ 。,{finish} [cmd]

可以將其解讀為“對從 {start} 開始,到 {finish} 結束的所有文字行,執行指定的 [cmd]”。

對於 :global命令與任意Ex命令的組合,都可以採用相同的正規化。例如,假設想對某一段指定範圍內的文字內容進行縮排,用Ex命令 :>(參見:h >)就可以實現。

➾ :g/{/ 。+1,/}/—1 >《 6 lines >ed 1 time 3 lines >ed 1 time

{注意:}

與 :sort不同的是,每當呼叫 :> 命令時,Vim都會提示一條資訊。如果在 [cmd] 的前面加上 :slient(參見 :h :sil ),就可以遮蔽這些資訊:

➾ :g/{/sil 。+1,/}/−1 >

此法尤其適用於 :g/{pattern} 匹配大量文字行的情況。

本文摘自《Vim實用技巧》(第2版)

Vim實用技巧:global命令

Vim是一款功能豐富而強大的文字編輯器,其程式碼補全、編譯及錯誤跳轉等方便程式設計的功能特別豐富,在程式設計師中得到非常廣泛的使用。Vim能夠大大提高程式設計師的工作效率。對於Vim高手來說,Vim能以與思考同步的速度編輯文字。同時,學習和熟練使用Vim又有一定的難度。

本書為那些想要提升自己的程式設計師編寫,閱讀本書是熟練掌握高超的Vim技巧的必由之路。全書共21章,包括123個技巧。每一章都是關於某一相關主題的技巧集合。每一個技巧都有針對性地解決一個或一類問題,幫助讀者提升Vim的使用技能。本書示例豐富,講解清晰,採用一種簡單的標記方法,表示互動式的編輯效果,可以幫助讀者快速掌握和精通Vim。

本書適合想要學習和掌握Vim工具的讀者閱讀,有一定Vim使用經驗的程式設計師,也可以參考查閱以解決特定的問題。