暫存器::Vim進階索引[4]

helloxchen發表於2010-10-22




Vim進階索引[4]::暫存器

以前經常要安裝剪貼簿的軟體來支援多次的剪下貼上操作。現在這些步驟可以省下了,Vim的暫存器就可以當成多個剪貼簿來使用。但是暫存器可不這麼簡單……

暫存器是Vim用來儲存文字的臨時空間。當我們使用y或d指令時被複制或刪除的文字會被送到暫存器,而我們可以透過p指令插入剛刪除或複製的內容。暫存器在這裡的作用就跟Window的剪貼簿相似,但Vim的暫存器要更多,作用也更多。

在Vim中不同暫存器有固定的名稱。我們可以透過這個名稱訪問它們的。暫存器的名稱由單個字元組成——只有一個例外,大部分的暫存器名稱是單個數字或字母有幾個特殊的暫存器以其他字元為名。使用時需要在暫存器名稱前加上"號以區別於一般命令以及標記(mark)。

:help registers
:help :registers
:help :copy-move
:help c_Ctrl-R
:help s=




1 數字與字母暫存器




1.1 數字暫存器

有些暫存器是有特殊作用的如數字暫存器。在介紹數字暫存器前先看一個命令:reg。現在輸入這個命令:reg。有沒有看到許多"號開始的數字或字元呢,這些就是暫存器。這裡面有你以前刪除的文字和最近複製的文字。

暫存器"0到"9就叫做數字暫存器。暫存器"0存著上一次複製操作所複製的文字。暫存器"1到"9分別儲存著你最近刪除的文字。"1的內容總是你上一次刪除的內容。每刪除一次這些暫存器的內容就往下傳遞。剛刪除的文字到了"1,而原來的暫存器"1的內容到了"2,原來"2的內容到了"3,……,原來"9的內容則被丟棄。數字暫存器只保留最近9條刪條的文字和一條複製的文字。

現在做一下下面的實驗,在Vim中分四行輸入123:

1
2
3

使用兩次dd指令,依次將1、2兩行刪除。輸入:reg觀察暫存器"1、"2。可以看到:

"1   2
"2   1

再使用dd指令將第三行刪除。再輸入:reg看看結果……現在你應該知道數字暫存器的工作方式了吧。

而我們經常使用的p指令,就是將最近一次刪除或複製的文字新增到當前位置。如果最近一次操作是複製則p就新增"0的內容,如果最近一次操作是刪除就新增"1的內容。




1.2 字母暫存器

現在看一下今天要講的第二種暫存器:字母暫存器(named register)。字母暫存器的名稱是單個英文字母。可以用這種方式表示一個字母暫存器:"a,"b,…,"z。同一個字母的大寫形式與小寫形式表示的是同一個暫存器,但它們在“行為”會有所不同,這點稍後說明。字母暫存器只有在使用者指定時才被使用。

一般模式(normal mode)下要訪問暫存器只要在使用複製和刪除指令y和d時,在前面加上暫存器的名稱即可。比如要將當前行及隨後兩行(1+2=3)複製到暫存器c中:"c3yy。要將剛儲存到暫存器c的內容“貼上”出來:"cp。數字暫存器也是一樣的使用方式,要將數字暫存器3的內容貼上出來:"3p。

提示:可是怎麼知道那個暫存器有自己想要的內容呢?使用:reg或:display。在命令後加上暫存器名稱則顯示相應暫存器的內容。

字母暫存器的名稱大寫時有特殊用途。當我們使用大寫的暫存器進行復制或刪除文字時,暫存器原來的內容會被保留,剛刪除或複製的內容則附加到原來內容的後面。如:`"Cdd‘時當前刪除行會新增到暫存器c原有內容的後面。大小寫的暫存器僅在複製和刪除時有區別。而當使用p時,大小寫暫存器名的作用是一樣的。

說明:在一般模式下Vim的刪除和複製命令相當靈活。有各種使用方式,下面是一些刪除命令的例子——將d改為y就是複製的例子了。如

3dd
刪除3行
d2l
向右刪除2個字元
v2]d
刪除兩個段落
df。
從當前位置刪除到句號
d`c
刪除至標記(:help :mark)c

除些之外x命令和p命令也有各種形式。所有這些命令及其不同形式都可以與暫存器一起使用,方法是在d、y、x、p前面加上暫存器。篇幅所限我們不可能將與暫存器一起使用的刪除複製命令一一列舉。雖然我們舉的例子者是yy或dd這樣刪除的最簡單形式,但無特別說明在與暫存器一起使用時yy和dd可以是任何刪除、複製或是貼上形式,以下同。詳細的刪除複製及貼上命令可以見:help deleting :help copy-move




2 其他暫存器

除了上面的兩種暫存器外,Vim還有很多種暫存器。這是Vim的文件中對暫存器的分類:

""
無名暫存器。儲存最近一次複製或刪除的文字。就是p命令預設使用的暫存器。

"-
短刪除暫存器(The small delete register)。事實上剛刪除的文字並不一定被送到數字暫存器,如果刪除的文字不含換行符(不足一整句)則文字被送至這個暫存器。如x、d2h這兩條命令刪除的文字都會被送到這個暫存器。注意下在這條命令雖然刪除了一整行的文字但因不含換行符所以也被送到這個暫存器`0d$‘。

": ". "% "#
只讀暫存器。它們分別用來儲存最近一次在命令列視窗使用的命令、最近一次插入的文字、當前編輯的檔名、當前的替代檔名。

"=
表示式暫存器。

"* "+ "~
選擇與拖放的暫存器。在Windows中這幾個暫存器就是剪貼簿。在Linux中它們也是剪貼簿——但這幾個暫存器是有所區別的。

"_
黑洞暫存器
刪除操作會影響現有數字暫存器的內容。前一個數字暫存器的值傳給後一個數字暫存器,"9的內容被丟棄,新刪除的文字則放入"1。這至少有兩個直接的影響,一是"9的內容被丟棄;二是暫存器中文字的位置都發生了變化。而複製操作會改變"0的值。如果你不希望刪除或複製的操作影響數字暫存器的話就使用這個暫存器。使用這個暫存器進行刪除或複製的內容都會被丟棄——這還可以提高一點速度節省一點空間。

"/
搜尋式樣暫存器。儲存上一次搜尋所使用的式樣。注意這也包括了s命令中所使用的搜尋式樣。




3 暫存器相關命令

前面已經說了一般模式下下的各種x、d、y、p命令都可以與暫存器一起使用,如"ayy。現在看一下在命令視窗中(或Ex模式)下怎麼訪問暫存器。
命令列中複製、刪除和貼上分別是`:y‘、`:d‘、`:pu‘。暫存器的使用方式是直接在上述命令後面加上暫存器的名稱——不需要在暫存器前加入"號。如:

:2,4y a
將第2至4行的文字複製到暫存器a中。
:'d A
將選中的行刪除並將其內容附加到暫存器a中。
:pu! a
將暫存器a的內容貼上到當前行之前。

暫存器也是一種變數可以在表示式中使用,因而也經常配合:exec構造複雜的命令。在替換命令:s的替換式樣部分可以使用表示式暫存器(:h sub-replace-expression)。一般模式命令q(:help q)可以用來錄製宏,而所錄的擊鍵序列就儲存在暫存器中。此外:redir命令也可以與暫存器一起使用。具體見Vim文件(:help :redir)。




4 暫存器的特殊性質

我們已經知道數字暫存器可以保留複製和刪除的內容供使用者使用。但這個功能實際用途已大不如前,因為Vim支援無限的undo操作。而且由於引入了Vim指令碼後可以使用變數來代替暫存器的作用。但暫存器並非毫無存在價值。




4.1 是臨時的儲存空間

這正是暫存器出現的目的。有時候我們需要一些臨時儲存空間我們就可以使用暫存器而不需要新建一個臨時檔案。比如寫作時你也許會發現有一整段的文字也許應該刪除或放到其他位置。這時你可以把它放到暫存器中。然後在需要時再把它貼出來——沒錯就象Windows的剪貼簿。但更好用,因為你有26個字母暫存器可以使用;可以使用大寫字母將文字附加到已有內容後。如果在你關閉檔案之前還沒想到這將這些內容貼在哪裡也沒關係用`:wviminfo my_viminfo‘命令。下一次編輯時輸入`:rviminfo! my_viminfo‘或者在命令列用這個命令執行`gvim -i my_viminfo myfile‘,:reg看暫存器的內容是不是都還在呢1




4.2 暫存器也是變數

在上一篇“指令碼”中我們說過了是個變數——特殊的變數,只要在前面加上一個@號就可以用變數的方式訪問暫存器。
所以,變數的操作也同樣適用於暫存器。

" 給暫存器賦值
let @e="開始"
let @E="結束"
echo @e
開始
結束
" 將暫存器作為表示式的一部分
let my_var=@a . @c
" 和
echo @e+4
" 清空暫存器。
" 注意:不能用unlet清除暫存器。
:let @e=""




4.3 在編輯視窗與命令視窗間交換內容

編輯視窗的文字可以放進暫存器。搜尋式樣和上一條Ex命令被放進了只讀暫存器"/和":。
已知暫存器的內容可以在貼到編輯視窗。可以在命令視窗作為變數使用。那有沒有辦法在命令視窗插入暫存器的內容呢?有沒有辦法在搜尋式樣中插入暫存器的內容呢?

比如,假設在暫存器e中儲存著一個檔名:“這是一個儲存在暫存器中的很長的檔名.txt”。而我想使用:w命令儲存一個當前編輯檔案的副本——使用暫存器e中的那個檔名。如果使用`:w @e‘的話,檔名將是“@e”而不是“這是一個儲存在暫存器中的很長的檔名.txt”。這時該怎麼辦呢?考慮到暫存器也是變數,我們可以使用暫存器的傳統辦法。

" 方法一。使用:execute命令
" 寫入以"e為名的暫存器中
:exe "w " . @e

那搜尋呢?如果我們要在搜尋式樣中使用暫存器的內容呢?對於s命令的搜尋式樣上面的:exe大法仍然適用,但如果只是普通的搜尋操作(在一般模式中按/)呢?我們要用到組合鍵Ctrl-R,用Vim的寫法就是

" 方法二。使用Ctrl-R轉義。
" 搜尋暫存器e的內容。表示使用者在這裡按了組合鍵Ctrl-R——不要直接輸入這8個字元。
/e/

使用的方式可適用於各種輸入的環境中:在插入模式輸入時、在命令視窗輸入時、在搜尋時。在插入模式時要輸入暫存器內容並不需要退回到一般模式再使用p指令,可以直接按`e‘當然e可以改成相應的暫存器名。在命令視窗與搜尋時也是一樣:按Ctrl-R輸入暫存器名。

提示:除了一些不接受變數作為引數,不能使用暫存器名稱的情況外,還有一些情況也要求插入暫存器的內容。有時我們插入暫存器的內容而不使用暫存器變數是因為我們可能還需要手工對暫存器的內容進行一些編輯。

無名暫存器總是儲存著最近一次複製或刪除的內容。不帶暫存器名地使用p就可以新增該暫存器的內容到當前位置了。但是既然“無名”該怎麼在命令視窗使用這個存器呢?又怎麼插入無名暫存器的內容呢?答案是使用@",插入也是一樣按Ctrl-R再按輸入"就可以了。

現在總結一下:":儲存了上一條Ex命令。"/儲存了上一條搜尋式樣。字母暫存器及數字暫存器中可以儲存編輯的文字。並且我們也可以在不同的環境中插入暫存器的內容。透過暫存器我們可以方便地在命令視窗編輯視窗以及搜尋中交換內容。相對而言一般的變數就沒這麼方便,你只能在命令列中使用變數也只能是命令列中給變數賦值。




4.4 在buffer之間及程式之間交換內容

暫存器是全域性的變數。在Vim中開啟的所有檔案2,共享這些暫存器。你可以在不同的檔案之間交換內容。

透過暫存器"*和"+,Vim可以與其他程式交換資訊。在Windows中這兩個暫存器是一樣的。在Linux中這兩個暫存器則有所不同。

:help gui-selections
:help x11-selection




4.5 暫存器可以做為宏

跟一般的變數相比暫存器還有一個最大的特點就是暫存器本身可以做為宏使用。如果你有用過一般模式命令q的話就會發現q錄製的擊鍵序列就是存在暫存器中的,並且可以直接使用暫存器執行命令。現在做做實驗,新建一文件隨便輸入幾行文字。輸入:

qeggddq

上面這條命令錄製了一個宏並儲存到暫存器e中。這個宏的作用是回到第一行並刪除該行。現在看一下暫存器的內容:

:reg e

就是你剛才的鍵盤命令ggdd。要執行剛錄製的鍵盤操作在一般模式輸入@e就可以執行了,輸入3@e會將前三行刪除。

當然你不一定要用q來錄製宏——因為暫存器也是變數。

:let @e="/刪除本行/^Mdd:w^M"
@e

上面的^M表示的是Enter鍵。可不是輸入^再輸入M,而是輸入Ctrl-V(Windows是Ctrl-Q)再按Enter鍵這時就會出現^M表示這是一個Enter鍵。常見的還有^[表示的是鍵。輸入的方法也是一樣按Ctrl-V再按Esc鍵。這樣輸入控制字元的方式是傳統的Vi方法。在Vim中也支援用按鍵名錶示這些控制字元。比如表示Enter鍵3所以上面的命令也可表示為:

:let @e="/刪除本行/dd:w"

這裡一定要用雙引號,我們在“指令碼”一篇中已經講到了,在單引號中的字串會被當成普通字串。後面這種表示控制字元的方式與'cpoptions'的設定有關,雖然在預設情況下都是可行的但是建議使用第一種方式。不過為了更好的可讀性在教程中我們還是可能使用後面這種方式表示控制字元。

正因為暫存器可以直接執行所以":可以用來執行上一條在命令視窗使用的命令:

:@:

記得最後要按回車執行。當然現在由於命令列的歷史功能這種用法沒有什麼實用價值。

關於宏的更多用法我們將另外解說。




4.6 在重定向命令中使用

重定向命令(:redir)是一個較常用的技巧。所有的字母暫存器、@*、無名暫存器(@")都可以在重定向命令中使用。還是用個例子說明好了:

假設你的小說家朋友寄了一本小說的初稿給你,但顯然他沒有整理文字的習慣——好訊息是他這次竟然沒用Word寫。在你往下看之前你決定先將文件做適當的整理。使用Vim作這種事當然是小菜一碟,只用了10分鐘你就將他的小說整理成一份格式整齊的文件了。

第六章 為山九仞
===============
	
  小明是從不在午時之前離開被褥的,今天卻是個例外。他一夜沒睡不
  過他卻覺得精神比任何時候都好……
	
  < 省略800行 >
  ……
	
第七章 功虧一簣
===============
	
  小明已經很久沒像今天這樣開心了。從那時到現在已有二十年又一天
  了。對他來講二十年並不長,能在二十年又三天之內報仇已經是出乎
  自己意料了。何況對方是可是威巒鏢局的大當家。想到這裡他的眼睛
  眯得更小了……再過兩天……只要兩天!
	

但你發現這份初稿沒目錄,而你看小說的習慣是從目錄看起。於是你決定整理一份目錄。於是你用了暫存器:

:let @a=""
:g/^第.{1,3}章 /y A

這兩條命令將所有章的標題放到暫存器A中。你可以在需要目錄的地方"ap。不過你還想在每章標題後加上該章對應的行號,你知道這時可以用:redir:

:redir @a
:echo "目錄:"
:g/^第.{1,3}章 /echo getline(".") . "ttt" . line(".")
:redir END

現在你的暫存器a中有了一個帶行號的目錄了。只用了幾行命令你就漂亮地完成任務了,想到這這裡你的眼睛眯得更小了……

注:這裡用到的函式是我們在講摺疊時說過的getline("."),表示返回當前行。line(".")則返回當前的行號。這兩個函式的詳細用法見文件。透過對這個指令碼進行擴充我們甚至可以讓它抓取含小節的目錄。

上面的例子演示了透過:redir使用者能對暫存器的內容進行進一步的加工而不只是簡單的摘錄,它增加了暫存器的使用範圍。這正是與redir之所以成為暫存器重要性質之一的重要原因。在Vim7.0之前的版本中不支援重定向內容到變數,所以暫存器成了唯一選擇。考慮到:redir是比較重要的命令,暫存器吃香也就不足為奇了。但在Vim7開始支援重定向內容到變數後,暫存器就沒那麼重要了——當然如果你希望方便地將重定向的內容插入到檔案中的話暫存器仍是理想之選。關於:redir的更多內容,將會另外解說。




4.7 表示式暫存器

雖說是暫存器但從各種角度來看這都是個冒牌的暫存器。它的主要作用是實時計算表示式的值。適用的場合:在編輯輸入時、在命令視窗輸入時、在搜尋時。使用的方式是按Ctrl-R再按等號(=),接著輸入表示式,原來輸入的位置就會插入表示式的值。只要是合法的表示式都可以使用。我們知道字串可以做為合法的表示式,所以在插入模式下按Ctrl-R =然後輸入"abc"(注意包括")當前位置就插入了abc。當然我們不會為了輸入字串而使用這個暫存器。現在暫存器a中儲存著一個數字,你想在當前文件中搜尋該數字4倍的另一個數,你當然不想自己計算。這時使用表示式暫存器:/=@a*4/ 。其中不是讓你輸入這8個字元而是按組合鍵Ctrl-R,同理表示這裡按了回車。任何時候當你需要插入一個表達示的值時都可以使用這個暫存器。

如果在輸入=號後直接按回車沒有輸入表示式的話預設使用上一次使用的表示式。

在上一個例子中,如果你把剛才的目錄貼在檔案的開頭(當然是開頭),會發現行號不準了因為所有的內容都被往下移了——第一行現在變成在目錄後面了。假設增加的目錄有25行(不知道有幾行?:se nu),現在文章的第一行(是空行)成了第26行。當然這樣問題難不倒你,讓表示式暫存器重新計算一下行號就行了——將原來的行號加上25。

注:下面幾個控制字元的輸入方式:^I, ^R, ^M, 分別表示的是Tab鍵,Ctrl-R,回車。它們的輸入方式是按Ctrl-V(或Ctrl-Q)再輸入各自所表示的鍵。

:1,25norm $T^I"ty$:s/[0-9]+$/^R=@t+25^M/^M

:1,25norm                                                 在1到25行之間(目錄區)執行一般模式命令
          $T^I                                             移到行末,將游標定位到最後一個製表符後(也就是第一個數字的位置)
              "ty$                                         將數字複製到暫存器t中
                  :s/[0-9]+$/                             將行末的數字(每一章的行號)
                              ^R=                          插入表示式
                                  @t+25                    將暫存器t中的數字加上25
                                     ^M                    插入回車結束表示式
                                         /^M               結束s命令並在插入Enter鍵
                                                    在全部輸入完成後別忘了按回車執行命令

還有一個特殊的地方可以用上表示式暫存器(Vim文件沒說這是一個表示式暫存器,但它的使用方式與表示式暫存器完全一樣),就是:s命令。:s命令的命令格式為::s/lhs/rhs/,表示搜尋lhs並替換為rhs。一個特殊用法就是當rhs的開頭為`=‘時,這rhs將被視為表示式。lhs將被替換為表示式的值。

:" 例:將當前行中的算術式'42x31'替換為算術式的結果。
:s/42x31/=42*31/

再回到剛才的例子,中現在我們可以用一種相對優雅的方式計算更新該目錄的行號:

:1,25s/[0-9]+$/=submatch(0)+25/

注:submatch()只在:s命令rhs的表示式中使用。submatch(0)與原來的&在rhs的作用是一樣的。submatch(1)就相當於原來rhs中的1,依此類推。




5 小結

當我們在進行互動編輯時,暫存器可以提高效率。不過正因為它的這樣互動特性,它並不經常在指令碼中使用。因為我們完全可以在指令碼中使用變數來替代暫存器。它的優點是能方便地與buffer的內容互動。當我們想把buffer的內容賦予某個變數時比較麻煩因為沒有直接的一般模式命令可以做到這時暫存器則更方便點。




Footnotes

[1] 通常暫存器的內容會保留到下一次開啟Vim,但不一定會保留到下一次開啟同一文件所以需要人為的儲存
[2] 準確地說是buffer,這與檔案的概念並不完全一樣
[3] :help key-notation

http://blah.blogsome.com/2006/04/27/vim_tut_register/

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24790158/viewspace-1040220/,如需轉載,請註明出處,否則將追究法律責任。

相關文章