今天在研究vim的autocmd命令時看到":help auto"的最後,協助文檔中舉了一個例子:在寫入一個 *.html 檔案時自動插入當前日期和時間。如下:
autocmd BufWritePre,FileWritePre *.html ks|call LastMod()|'s
fun LastMod()
if line("$") > 20
let l = 20
else
let l = line("$")
endif
exe "1," . l . "g/Last modified: /s/Last modified: .*/Last modified: " .
\ strftime("%Y %b %d"
)
endfun
隨後解釋了這段代碼:
要這段代碼工作,你需要在檔案開始的 20 行裡有這行 "Last modified: <date
time>"。 Vim 把 <date time> (包括該行其後的任何內容) 替換為當前的日期和時間。
解釋:
ks 儲存當前位置到 's' 標記
call LastMod() 調用 LastMod() 函數完成工作
's 游標回到舊的位置
LastMode() 函數先檢查檔案是否少於 20 行,然後用 ":g" 命令尋找包含 "Last
Modified:" 的行。在這些行上執行 ":s" 命令實現從已有的時間到目前時間的替換。
":execute" 命令使 ":g" 和 ":s" 命令可以使用運算式。日期用 strftime() 函數取
得。它可以用別的參數取得不同格式的日期文字。
看到這,我突發靈感:這幾天一直頻繁更改自己的vim設定檔_vimrc,每次修改完都要手動去修改檔案頭的最後修改時間,何不用這段代碼把自己解脫出來呢?於是把以上代碼拷到我的_vimrc裡,並把第一句改成:
autocmd BufWritePre,FileWritePre _vimrc ks|call LastMod()|'s
然後把檔案頭部的最後修改時間那一行改成如下:
" Last modified: <date time>
儲存,重啟vim,開啟_vimrc,運行:w<CR>,這一行果然自動變成:
" Last modified: 2007 九月 06
嗯,日期有了,還差個時間,自然想到去該一下那個LastMod()函數,這時意外的發現:
exe "1," . l . "g/Last modified: /s/Last modified: .*/Last modified: " .
\ strftime("%Y %b %d")
變成了
exe "1," . l . "g/Last modified: 2007 九月 06
\ strftime("%Y %b %d")
稍加思索後,我明白了,因為這個函數也在前20行所以在儲存自動插入時間時把這句中的Last modified: 也給替換了。
最簡單的解決辦法就是把這個函數弄到20行之後。但如果設定檔裡只有很少的配置項,我就不得不在函數前加入好幾行空白行。琢磨來琢磨去,這個例子函數存在很大的問題。於是決定將它修改一番,使其沒有任何限制。
先來分析一下函數中關鍵的一句:
exe "1," . l . "g/Last modified: /s/Last modified: .*/Last modified: " .
\ strftime("%Y %b %d")
經過上面的if語句,此時變數L已經變成了_vimrc檔案的行數(如果行數不到20)或20。那麼這句話的意思是:從第一行到第L行,先用g命令找到與Regex“/Last modified: /”匹配的一行,再用s命令把與Regex“/Last modified: .*/”匹配的內容替換成“Last modified: 當前日期”,其中strftime函數返回的就是當前日期。
而這個行數正好在第1到L行內,並且這一句正好匹配“/Last modified: .*/”,故,除了我們想要的那一行被替換了之外,這一行(exe ...)也被替換。
思來想去,勢必要把“g/Last modified: /”用另一種方式表達才不致匹配自己,後面的兩個“Last modified”也要變一下,試來試去,終於這樣可以了:
exe "1," . l . "g/[L]ast modified: /s/[L]ast modified: .*/Last modified:[不要這裡的空格]" .
\ strftime("[在這裡添加空格] %Y %b %d")
後來發現g命令是多餘的(我武斷了,其實是有用的,見一樓評論),再加上時間,最終變成了這樣:
exe "1," . l . "s/[L]astModified: .*/LastModified:" .
\ strftime(" %Y %b %d %X")
這樣,這個函數沒有了所在行的限制,故上面的if語句也應改一下,函數最終變成這樣(函數名也順便改了一下):
un LastModified()
let l = line("$")
exe "1," . l . "s/[L]astModified: .*/LastModified:" .
\ strftime(" %Y %b %d %X")
endfun
是不是精簡了很多,並且比原來的要更具有可擴充性,和松耦合性。
搞Regex順了手,乾脆把第一句的autocmd也改了:
autocmd BufWritePre,FileWritePre [._]vimrc ks|call LastModified()|'s
這樣,無論開啟的是.vimrc還是_vimrc都可以在儲存時自動插入修改日期和時間了。
最終成果如下:(評論第9樓才是最終成果)
autocmd BufWritePre,FileWritePre [._]vimrc ks|call LastModified()|'s
fun LastModified()
let l = line("$")
exe "1," . l . "g/[L]ast modified: /s/[L]astModified: .*/LastModified:" .
\ strftime(" %Y %b %d %X")
endfun
PS:萬一,(一萬分之一,當然幾率很小)在檔案的其他地方恰巧也出現了LastModified,並且那一行也恰巧與Regex/[L]astModified: .*/匹配,那你可的小心了。^_^