以前有人問過我,有沒有什麼方法可以在程式的每一行前面補上所在的行數?我的答案是:

cat -n input_filename > output_filename

他又問「如果沒有 cat 怎麼辦?可以用 vim 完成嗎?」因為我所提供的方法對於 Un*ix 的使用者來說是非常方便的,但是對其他作業環境的使用者可就未必了。那時候我想了一下,總覺得需要一個計數器,從頭開始算,然後把數字補在每一行的前面。雖然說 vim 有提供 :set nu 的功能,就像下面這兩張圖:
下面這張是原來的樣子
20070416vim-line-examle.jpg

做過:set nu 之後
20070416vim-line-examle-with-nu.jpg

但是在windows 上你卻不能把它一行行 copy 下來用。不想搞程式,又想要一行解決,有沒有呢?

答案是必須要靠 vim 內建的函數來幫忙,也就是今天介紹的 line() 和 submatch()。

我們先來看答案:

:%s/^.*$/\=line(”.”) . ” ” . submatch(0)/g

在 vim 裡面, line() 就是用來代表行數的。而line()裡面的 “.” 則用來表示目前游標所在的地方,換言之,也就是處理到哪一行游標就在那。而 submatch(0) 則是用來表示前面所尋找的整個字串(pattern),而submatch(1)的話,則用來表示第一個以 \(…\) 夾起來的子字串。

不過由於我們所採用的是取代 s,s 的語法本來是 s/pattern1/pattern2/option,但是它提供在 pattern2的地方可以作一些運算。但是開頭必須要用 “\=” 來開始,否則就視之為字串,而且在這個情況下,\1 \2 這種特殊字串所代表的特殊意義都不能使用。一些細節你可以參考

:h sub-replace-expression

所以答案裡面的 pattern2 的部份就是先用 \= 做開始,表示要做運算,「”」這個符號夾起來的表示字串,而字串要和運作的結果相連的話,要用「.」來接。如果你的字串需要「”」這個符號,則用「\」補在前面,也就是變成「\”」。

結果就會像下面看到的一樣:
下取代指令:
20070416vim-line-examle-linecommand.jpg
最後需要的結果:
20070416vim-line-examle-result.jpg