2016/02/29

vim 下移除重複的行

在讓電腦列出1了一份所內含的中文字體清單之後,發現有些重複的內容,因此想要將重複的行去除,並稍微排列一下。於是上網找在 Vim 下操作的方法。

以下的內容是 google 到一份2004年在 Vim Wikia 上的文章2,實作並修正過後的內容。

先排序

:sort
這個指令會先將檔案內容進行排序。若不先排序,以下的指令都没有用。
 

查找相同內容的行

接下來則是要在排序過後的內容中找出內容重複的前後行。
/^\(.\+\)$\n\1
排列後的搜尋結果
這個指令,可以查到多個排序過了的、內容一模一樣的前後行,不只是兩個內容相同的行而已。但它原本的意思是查找前後相鄰兩行的內容是否相同。 
 

移除重複內容的行

確認(按 n)過查找的東西没錯之後,就可以逕行刪除。 
 
:g/^\(.\+\)$\n\1/d 
 
但是,這個指令有點小問題。因為排列的時候,比較短的行會排在前面內容一樣,而後面還有多出一些內容的行的前面;使用這個命令,會使得短的那行被刪除掉。因為這個指令的結果是刪除前面重複行而保留後面的那行。
短的相同行被刪除
所以,$ 應該排在替換指令 (d) 前面,而非換行符 (\n) 前方。當然,在查找的過程中,$ 的前後位置應該没什麼差異。 

為了避免遺憾,必須要用以下的命令來操作:

:g/^\(.\+\)\n\1$/d
或是
:g/\%\(^\1\n\)\@<=\(.\+\)$/d 
短的相同行没被刪除
 
後者不同的地方在於刪除後面比對過的重複行,而保留最前面的那一行。

Vim Wikia 還提供了一個描述中非常神奇的指令,它省略了 a@<=b 模式中部分的反斜線:

:g/\v%(^\1\n)@<=(.*)$/d
 
最後有個小問題,在 Vim Wikia 文章中所用的指令,實際上是
:g/\%(^\1\n\)\@<=\(.*\)$/d
 
在百分號之後的小括號前未加反斜線 (blackslash,\),在我的 Mac OS X 中,Vim 竟然會跳出警告訊息:
:Unmatched \%(
:Invalid command
 
所以,我只得讓 \%( 變成 \%\( 好讓指令運作正常。
 
[補充]
- 20160309:修飾語句,增加指令意思的說明。

  1. fc-list -f "%{family}\n" :lang=zh > output-file.txt 
  2. http://vim.wikia.com/wiki/Uniq_-_Removing_duplicate_lines。