VIM的搜尋取代插入進階用法


VIM的搜尋取代插入進階用法



    VIM中的基本搜尋方式是在普通模式下按 “/” 鍵(指令模式的),再輸入要搜尋的字串或是正則表示式,就可以找到需要的字串。

    透過搜尋的結果可以驗證輸入的條件或正則表示式是否可以正確選出要處理的字串。例如以下的檔案內容




冰ice

火fire

水water

車子car

房子house




如果要選出英文字母的部份,可以在普通模式下輸入 
“/\w”。其中的“/”是進入搜尋模式,“\w”指的是正規表示式中的[0-9A-Za-z_],所以範例中的所有英文字母都會被選取起來。想知道在
VIM中有多少個類似“\w”的用法可以在普通模式下輸入“:h \w”,就可以看到一張列表和說明了。



搜尋成功之後,接下來就是取代的功能了。取代的說明可以在指令模式下輸入“:h
substitute”,可以看到取代指令的基本格式是“:[range]s[ubstitute]/{pattern}/{string}
/[flags]
[count]”。不過目前這次會用到的只有“:[range]s[ubstitute]/{pattern}/{string}/”而已,有興趣的朋友
可以看看說明檔裡的說明。



我們試著將每一行的第一個英文字母變成A,指令為“:%s/\w/A/”

其中%指的是[range]的部份,也就是檔案全部,如果沒有指定[range]的話,範圍就是游標所在的那一行。%後面的s,就是s[ubstitute]的s,“\w”就是這次的{pattern},也就是搜尋條件,“/A/”就是{string},要換上去的字串。執行了“:%s/\w/A/”後,檔案內容應該就會變成以下的樣子:




冰Ace

火Aire

水Aater

車子Aar

房子Aouse




可以看到每個字的第一個字母都變成A了。



接下來就是插入的功能了,這個功能平常比較少在用,但卻是相當實用的功能。現在先回到最原始的檔案內容:





冰ice

火fire

水water

車子car

房子house








    現在我們試著在英文和中文的中間插入一個等號。

    指令為“:%s/\(\w\)/=\1/”

    其實這個指令的意思是在每行第一個字母前加上一個等號。其結果如下




冰=ice

火=fire

水=water

車子=car

房子=house




    “:%s/\(\w\)/=\1/”指令中的“%s/”就跟之前一樣,就不再說明,\(\w\)中的\w”和之前的說明一樣指的是指的是正規表示式中的[0-9A-Za-z_],而\(\w\)中的\( \)”把\w”包起來,指定這是一個範圍,在一個搜尋的{pattern}中可以有好幾個範圍,之後要引用時就是用出現的順序來調用,例如第一個就用\1”,第二個就用\2”



    接下來的是{string}部位的=\1=\1”的\1指的就是{pattern}中出現的第一個指定範圍,也就是\(\w\)”,在這個例子中只有一個指定範圍,所以就只有\1”出現而沒有\2”。而=\1=就是在找到的指定範圍前面加一個=。或許有些人會覺得奇怪,明明用\(\w\)沒有指定出現個數的話,搜尋到的結果應該是每一個字元”,所以應該是在每一個字元前面都加上=才對,問題就出在取代指令的基本格式是“:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]”中的[flags],在這次使用的“:%s/\(\w\)/=\1/”取代指令中可以看到並沒有指定[flags],在[flags]沒有指定的情況下,只會針對每一行第一個符合條件的字串做處理,之後的就跳過了。如果要讓每一個字元前面都加上=的話,[flags]就用g”,整個指令就會變成 “:%s/\(\w\)/=\1/g”。有興趣的朋友可以自己試看看





    再來就是多個指定範圍的使用方法,以下把原始範例中的中文和英文做對調來做示範。

    指令為“
:%s/\(\W\+\)\(\w\+\)/\2\1/

    效果如下:




ice冰

fire火

water水

car車子

house房子




    這個部份稍嫌複雜,做個對照

    指令 “:%s/\(\W\+\)\(\w\+\)/\2\1/

    格式 “:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]

    用顏色來區分可以比較清楚看出各是那個參數,其中的[flags] [count],在這裡是沒有用到的。

    %s請看前段,就不再說明囉!

    \(\W\+\)\(\w\+\)是參數中的{pattern},大寫的W所代表的字元就是小寫w的相反,相關說明可以參考:h \W”。在這裡的用途就是用來選出中文的部份。眼尖的朋友應該有注意到W後面的“\+,這表示至少重複一個以上(類似用法可以參考:h multi”)

    用“
\(\)括起來表示這個範圍指是一個或一個以上的不是0-9A-Za-z(VIM中的說明是指“[^0-9A-Za-z_],在這個簡單的例子中指就是中文)的字元集合,簡單的說就是一個以上(含一個)的中文字。\(\W\+\)指的就是冰”,“火”,水”,車子”,房子”

    相同而言“\(\w\+\),就是指ice”,“fire”,water”,car”,house”



    \2\1({string})中的\2指的是{pattern}中的第二個指定範圍,也就是第二個\(\)括起來的部份,在這裡指的就是\(\w\+\)(ice”,“fire”,water”,car”,house”)。而“\1就是第一個\(\)括起來的部份,指的是\(\W\+\)”(冰”,“火”,水”,車子”,房子”)。\2\1的順序就是把第二個範圍跟第一個範圍做對調,就可以達到中文和英文的對調了。







    在上面的例子中,如果沒有\+的話,就會變成以下的結果。




i冰ce

f火ire

w水ater

車c子ar

房h子ouse




    這個地方就留給大家自己想一想為什麼了?


6 則留言:

  1. 說到正規表示法,我個人覺得應該可以稍微提到 grep sed這兩個常用指令。能提到 awk 會更好。
    無論如何,這篇文章還是寫的很棒。加油!

    回覆刪除
  2. 謝謝你的回應,因為我的sed和awk實在太少用,所以一整個不行。

    回覆刪除
  3. 你寫得好清楚喔!謝謝你。
    :)

    回覆刪除
  4. 感謝你的回應!

    回覆刪除
  5. 很高興我的筆記能對你有幫助.....

    回覆刪除

dnf upgrade fails with Error: GPG check FAILED

 OS: Fedora 36 今天在做  dnf  更新的時候,突然有很多 package 都出現簽章問題無法更新。類似如下的錯誤訊息 .... is not signed. ...... is not signed. ......... is not...