1. 程式人生 > >程式設計小技巧之 Linux 文字處理命令(二)

程式設計小技巧之 Linux 文字處理命令(二)

合格的程式設計師都善於使用工具,正所謂君子性非異也,善假於物也。合理的利用 Linux 的命令列工具,可以提高我們的工作效率。 本篇文章是[《Linux 文字處理命令》](http://remcarpediem.net/article/45ef130c/) 續篇,在前文的基礎上再介紹幾款有用的 Linux 命令列工具和使用場景。 再囉嗦幾句,工具能提供效率,但是有一定的學習曲線和學習成本。很多同學臨時想用時,可能會陷入了不會用的尷尬境地,再去網上搜索學習,最終要花費更長時間,還不如使用笨方法處理,這是很多同學不使用這些工具的原因之一。 而且更難的是,思維上改變原有的做事習慣,一個檔案中有20多行資料要生成 SQL,好像用手工處理也就是1,2分鐘;快捷鍵記不住,我滑鼠移動一下點點也挺快。但是當行數量變大或者複雜性提高時,這些手段的弊端就會顯現,逼迫我們去使用正確的手段。 所以,為什麼不一開始就使用更快,而且可以處理更加複雜場景的手段呢? 本文主要以兩個場景為引子,介紹 join、sort、uniq 命令和 sed 編輯器。 #### 合併兩個檔案中的關聯行 簡單說一下場景,有兩個檔案,裡邊都是固定格式的行,代表著資料庫的一行資料,一個檔案是使用者相關的資料,有 user_id、username 和 gender 三列,另外一個檔案是訂單相關的資料,有order_id、price、user_id,time四行,現在要按照 user_id 將兩個檔案按行合併,也就是user_id相同的行組合成一個新行,如下圖所示。 ![](https://img2020.cnblogs.com/blog/1816118/202101/1816118-20210128213607091-1028347152.png) 上述兩個檔案的內容如下所示: ``` // order.txt user_id是第三列 o1 1 u1 2011-9 o2 2 u2 2011-10 o3 3 u3 2011-10 o4 4 u1 2011-12 // user.txt user_id是第一列 u1 tom 男 u2 jack 男 u3 nacy 女 ``` 我們準備使用 join,發現具體命令格式已經忘記了。這時,既可以去網上搜尋,也可以去詢問 man ![](https://img2020.cnblogs.com/blog/1816118/202101/1816118-20210128213620669-253094537.png) 通過 man 你可以瞭解到 join 的功能描述和引數介紹,一般來說看 DESCRIPTION 一欄下的即可。 ![](https://img2020.cnblogs.com/blog/1816118/202101/1816118-20210128213632991-1124751161.png) 從上邊的 man 文件可以很清楚的瞭解到 join 命令使用 equality join 操作對特定的檔案進行合併,並輸出到標準輸出流上。join filed 就是用於合併檔案時進行比較的列,預設是兩個檔案的第一列。可以使用 -1 和 -2 分別制定第一個檔案和第二個檔案要對比的列。 ``` $ join -1 3 -2 1 order.txt user.txt # 指定以order.txt的第三列和user.txt的第一列來進行對比join u1 o1 1 2011-9 tom 男 u2 o2 2 2011-10 jack 男 u3 o3 3 2011-10 nacy 女 ``` 會發現,輸出中少了一行,order.txt 明明是四行,這是為什麼呢?我們再來細看 man 文件,發現了端倪。 ![](https://img2020.cnblogs.com/blog/1816118/202101/1816118-20210128213647431-788014973.png) 兩個合併檔案的行必須先按照對比列進行排序,否則可能會導致缺失部分行。user.txt 已經按照其第一列排序了,所以,我們只需要使用 sort 命令對 order.txt 按照其第三列進行排序。 sort 命令將以預設的方式將文字檔案的第一列以ASCII 碼的次序排序,並將結果輸出到標準輸出。-k 引數可以指定其根據第幾列進行排序。 ```bash $ sort -k 3 order.txt # 按照數字排序則使用 -n 如果反序則用 -r o4 4 u1 2011-12 o1 1 u1 2011-9 o2 2 u2 2011-10 o3 3 u3 2011-10 ``` 我們將上述兩個命令結合起來使用,現將 sort 的結果存入 sorted_order.txt,然後再進行 join,就能得到最終的結果。 ```bash $ sort -k 3 order.txt > sorted_order.txt $ join -1 3 -2 1 sorted_order.txt user.txt u1 o4 4 2011-12 tom 男 u1 o1 1 2011-9 tom 男 u2 o2 2 2011-10 jack 男 u3 o3 3 2011-10 nacy ``` 另外,上述命令預設的列分隔符都是`\t`和空格,可以使用 -t 引數來制定字元作為分隔符。 通過上述命令的組合,我們就完成了按照相同列合併兩個檔案的操作,這也體現了 Linux 的 KISS 思想,每個工具只做一小件事情。 還是基於上述的場景,突然需要統計一下 order.txt 中每個使用者購買的訂單數量,然後按照訂單數進行從大到小排序,這又該如何處理呢? 我們可以將 sort 和 uniq 兩個工具結合起來。uniq 命令一般用於檢查和刪除檔案中重複出現的行,我們可以使用它來計算 order.txt 中使用者出現的次數。 ```bash $ sort -k 3 order.txt | uniq -f 3 -c # -f 表示按照第三列進行統計 1 o4 4 u1 2011-12 1 o1 1 u1 2011-9 2 o2 2 u2 2011-10 ``` #### 刪除 Markdown 檔案中的超連結 另外一個場景是我編輯文章時遇到的,當時 markdown 格式的文件中有很多超連結,也就是`[描述](連結)`格式,希望全部把超連結去掉,也就是去掉方括號、圓括號和圓括號中的內容。因為文件中還有很多程式碼,包含很多圓括號語句,所以必須先準確超連結格式,然後再進行替換。 ![](https://img2020.cnblogs.com/blog/1816118/202101/1816118-20210128213707352-357282241.png) 這裡,我們可以使用 sed 命令。sed 的全名叫 stream editor 流編輯器,可以使用程式的方式來編輯文字。想要全面學習它的小夥伴可以閱讀 [《SED 簡明教程》](https://coolshell.cn/articles/9104.html)或 [《sed 手冊》](http://www.gnu.org/software/sed/manual/sed.html),我們這裡只介紹一下最基礎的功能,向大家展示使用它的可能性。使用 sed 一般要了解正則表示式,推薦[《正則表示式30分鐘入門教程》](https://deerchao.cn/tutorials/regex/regex.htm)。 sed 最簡單的使用方法就是替換文字。比如,我們要將上述的 order.txt 中的 u全部替換為user,則可以使用如下命令。 ```bash $ sed 's/u/user/g' order.txt # u是被替換的詞 user是替換詞 o1 1 user1 2011-9 o2 2 user2 2011-10 o3 3 user3 2011-10 o4 4 user1 2011-12 ``` sed 還能輕易實現 sublime 或者 vscode 經常使用的多行游標編輯的功能。比如在 order.txt 的每行前頭前新增文字。 ```bash $ sed 's/^/#/g' order.txt # ^在正則表示式中表示一行開頭,所以表示是在行開頭上加上#字元 #o1 1 u1 2011-9 #o2 2 u2 2011-10 #o3 3 u3 2011-10 #o4 4 u1 2011-12 ``` 下面,我們直接來看如何將超連結格式轉換為純文字。 ```bash $ echo "[連結](http://http://remcarpediem.net/)" | sed -E "s/\[(.*)]\(.*\)/\1/g" 連結 ``` 首先,識別`[描述](連結)` 格式的正則表示式是`\[.*\]\(.*\)`,其中 `\[`和`\(` 分別表示匹配文字的`[`和`(` 符號。`.` 表示任何單個字元,`*`表示某個字元出現了0次或多次, 二者組合 `.*` 則表示出現0次或者多次任何字元。綜上,上述正則表達的含義就是先出現一個`[`,再出現0次或者多次任意字元,在出現一個`]`,在出現一個`(`,在出現0次或者多次任意字元,最後出現一個`)`。 其次,我們希望用`[描述]`中的描述文字來替換整個超連結文字,所以需要先識別出方括號中的內容,則需要將其用`()`單獨括起來,表示一個子表示式,也就是`\[(.*)\]\(.*\)`。 最後,sed 的 `s///g` 模式下,s 表示替換模式,g 表示匹配每一行有行首到行尾的所有字元,加 g 則一行有多個連結可以匹配處理,不加只能匹配第一個。`\1`代表第一個子表示式,也就是方括號中的描述內容。 ![](https://img2020.cnblogs.com/blog/1816118/202101/1816118-20210128213547535-14021882