1. 程式人生 > 程式設計 >程式設計小技巧之 Linux 文書處理命令

程式設計小技巧之 Linux 文書處理命令

合格的程式設計師都善於使用工具,正所謂君子性非異也,善假於物也。合理的利用 Linux 的命令列工具,可以提高我們的工作效率。

本文簡單的介紹三個能使用 Linux 文書處理命令的場景,給大家開闊一下思路。希望大家閱讀完這篇文章之後,要多加實踐,將這些技巧內化到自己的日常工作習慣中,真正的提高效率。內化很重要,就像開玩笑所說的一樣,即使我知道高內聚,低耦合的要求,瞭解 23 種設計模式和 6 大原則,熟讀程式碼整潔之道,卻仍然寫不出優秀的程式碼。知道和內化到行為中區別還是很大的。

能不能讓正確的原則指導正確的行動本身,其實就是區分是否是高手的一個顯著標志。

程式設計師日常工作中往往要處理一些資料和文字,比如說統計一些服務日誌檔案資訊,根據資料庫資料生成一些處理資料的SQL和搜尋檔案內容等。可以直接通過編寫程式碼處理,但不夠便捷,因為有時候線上相關的程式碼環境依賴不一定具備。而直接使用 Linux 的文書處理命令可以很方便地處理這些問題。

日誌檔案撈資料

在工作中,我們往往需要對一些具有固定格式的檔案進行資訊統計,比如說根據 nginx 的 access.log 檔案資料,計算出每個後端 API 介面的呼叫次數,並且排序。

nginx 的 access.log 檔案檔案格式配置如下所示,每個欄位之間通過空格分隔開來。

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"'
; 複製程式碼

上述配置中欄位含義如下:

  • $remote_addr : 傳送請求的源地址
  • $remote_user : 傳送請求的使用者資訊
  • $time_local : 接收請求的本地時間
  • $request : 請求資訊,比如說 http 的 method 和 路徑。
  • $status : 請求狀態,比如說 200、401或者 500。
  • $body_bytes_sent : 請求 body 位元組數。
  • $http_referer : 域名。
  • $http_user_agent : 使用者端 agent 資訊,一般就是瀏覽器資訊
  • $http_x_forwarded_for : 其他資訊。

具體的一段 access.log 內容如下所示。

58.213.85.34 - - [11/Sep/2019:03:36:11 +0800] "POST /publish/pending/list HTTP/2.0" 200 1328 "https://remcarpediem.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/75.0.3770.142 Safari/537.36"
58.213.85.34 - - [11/Sep/2019:03:36:30 +0800] "GET /publish/search_inner?key=test HTTP/2.0" 200 34466 "https://remcarpediem.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/75.0.3770.142 Safari/537.36"
複製程式碼

那麼,我們可以通過下面命令來統計所有介面呼叫的次數,並且從大到小排序顯示。

cat /var/log/nginx/access.log | awk '{print $7}' | awk -F'?' '{print $1}' | sort | uniq -c | sort -nr
複製程式碼

這條命令涉及了 cat、awk、sort , uniq 四個命令列工具和 | 連線符的含義,我們依次簡單講解一下它們的使用,感興趣的同學可以自行去全面瞭解學習。

cat 命令是將檔案內容列印到標準輸出裝置上,可以是終端,也可以是其他檔案。比如說:

cat /var/log/nginx/access.log # 列印到終端
cat /var/log/nginx/access.log > copy.log # 列印到其他檔案中
複製程式碼

| 符號是管道操作符,它將的一個命令的 stdout 指向第二個命令的 stdin。在這條命令中 | 符號將 cat 命令的輸出指向到 awk 命令的輸入中。

awk 是貝爾實驗室 1977 年搞出來的文字流處理工具,用於對具有固定格式的檔案進行流處理。比如說 nginx 的 access.log 檔案,它各個欄位之間通過空格分隔開來,awk 就很適合處理此類檔案。

'{print $7}' 就是 awk 的指令宣告,表示打印出變數$7$7則是 awk 內建的變數,代表按照分隔符分隔開來的第七個文字內容。對於 access.log 檔案來說就是 $request 代表的路徑相關的內容。 $request 的全部內容是POST /publish/pending/list HTTP/2.0$6 對應 POST,而 $7 對應的就是 /publish/pending/list

awk '{print $7}' Access.log # ''中是命令宣告,後邊跟著要操作的檔案,也就是awk的輸入流。
複製程式碼

但是有些時候我們發現文字內容並不是按照空格進行分隔的,比如說 $request 內容可能為 /publish/search_inner?key=test,雖然是相同的 path,但是 query 不同,我們統計介面呼叫量時需要將 query 部分過濾掉。我們可以使用 awk 的 -F 指令指定分隔符。

awk -F'?' '{print $1}' 
# 可以將 /publish/search_inner?key=test 處理為 /publish/search_inner
複製程式碼

sort 是專門用於排序的命令,它有多個引數:

  • -n 按數值進行排序,預設是按照字元值排序,按照數值比較 10 > 2 但是按照字元值排序,2 >10 ,因為字元值會先比較首位,也就是 2 > 1。
  • -r 預設是升序排列,這個引數指定按照逆序排列。
  • -k N 指定按第N列排序,預設是第一個值
sort -nr Access.log # 按照數值逆序排序
複製程式碼

最後一個命令是 uniq,它用於消除重複行,或者統計。

sort unsort.txt | uniq #  消除重複行
sort unsort.txt | uniq -c # 統計各行在檔案中出現的次數,輸入格式是[字數] [內容]
sort unsort.txt | uniq -d # 找出重複行
複製程式碼

比如說cat /var/log/nginx/access.log | awk '{print $7}' | awk -F'?' '{print $1}' | sort | uniq -c 命令的輸出如下所示,正好作為 sort -nr 的輸入。

5 /announcement/pending/list
5 /announcement/search_inner
複製程式碼

利用這些指令,我們可以通過 access.log 統計很多資訊,比如下列這些資訊( access.log 的資訊配置不同,不可以直接照搬 )。

cat access.log | awk -F ‘^A’ ‘{if($5 == 500) print $0}’ 
#查詢當前日誌檔案 500 錯誤的訪問:
tail -f access.log | awk -F ‘^A’ ‘{if($6>1) print $0}’ 
#查詢耗時超過 1s 的慢請求
複製程式碼

資料庫SQL

在業務迭代過程中,有些資料庫資料可能需要使用指令碼去修改,這是我們可以要根據一些資料生成對應的 SQL 命令,這裡我們可以使用命令列工具快速生成。 比如說我們要將一系列訂單狀態有問題,需要將其恢復成正常的狀態。你現在已經收集到了這批訂單的資訊。

oder_id name info good_id
100000  '褲子' '山東' 1000
100001  '上衣' '江蘇' 1000
100002  '內衣' '內蒙古' 1000
........
100003  '襪子' '江西' 1000
複製程式碼

那麼你可以使用如下命令直接生成對應的 SQL 語句。

cat ErrorOrderIdFile | awk '{print"UPDATE ORDER SET state = 0 WHERE resource_id = "$1}'
複製程式碼

這裡 ''中都是 awk 的命令內容,而""中是列印的純文字,所以我們可以將需要補充的 SQL 命令打印出來。

程式碼資訊統計

在大公司中,各個團隊往往會公開出自己的介面給兄弟團隊呼叫,但是隨著版本地快速迭代,公開的介面越來越多,想要關閉掉又往往不清楚上游呼叫方是哪個部門的,輕易不敢關閉或者修改。這時,如果你能訪問整個公司的程式碼庫,就可以通過下面的指令碼搜尋一下專案中是否出現該介面相關的關鍵詞。

筆者公司團隊中微服務間通過 FeignClient 相互呼叫,所以對於這種情況,可以直接將搜尋出對應 FeignClient 的函式名出現的檔名稱。

下面是一段在多個專案中統計某些關鍵詞出現次數,並打印出檔名的 bash 指令碼。

#!/bin/bash
keyword=$1 # 將bash命令的第一個引數賦值給 keyword
prefix=`echo $keyword | tr -s '.' '|' | sed 's/$/|/'` # 處理字首
files=`find services -name "*.java" -or -name "*.js" | xargs grep -il $keyword` 
# 最關鍵的一條,搜尋services資料夾下檔名字尾為.java或者.js並且內容中有關鍵詞的檔名稱。
if [ -z "$files" ];then
	echo ${prefix}0
fi
# 列印
for f in $files;do
echo "$prefix$f"
done
複製程式碼

我們只看一下最關鍵的 find 命令,其他的命令比如 tr 或者 sed,大家可以自行了解學習。

find 用於查詢檔案,可以按照檔名稱、檔案操作許可權、檔案屬主、檔案訪問時間等條件來查詢。

find services -name "*.java" -or -name "*.js" # 搜尋 services 資料夾下
find . -atime 7 -type f -print 
# -atime是訪問時間,-type 是檔案型別,區分檔案和目錄,查詢最近7天訪問過的檔案。
find . -type f -user remcarpediem -print// 找使用者 remcarpediem 所擁有的檔案
find . ! -name "*.java" -print # !是否定引數,查詢所有不是以 .java 結尾的檔案。
find . -type f -name "*.java" -delete # find 之後的操作,可以刪除當前目錄下所有的 java 檔案
find . type f -name "*.java" | xargs rm # 上邊語句的另外一種寫法
複製程式碼

xargs 命令能夠將輸入資料轉化為特定命令的命令列引數,比如說多行變一行等,串聯多個命令列,比如說上邊 find 和 rm。

> ls 
Sentinel					groovy-engine					spring-cloud-bus-stream-binder-rocketmq
agent-demo					hash						spring-cloud-stream-binder-rabbit
> ls | xargs # 將 ls 的輸出內容變成一行。
Sentinel agent-demo groovy-engine  hash spring-cloud-bus-stream-binder-rocketmq spring-cloud-stream-binder-rabbit
> echo "nameXnameXnameXname" | xargs -dX 
name name name name
# -d 選項可以自定義一個定界符,相信你已經瞭解 xargs 的大致作用了吧,按照分隔符拆分文字到一行,預設分隔符當時是回車了。
複製程式碼

最後一個命令時 grep,它是文字搜尋命令,它可以搜尋文字內容的關鍵詞。

grep remcarpediem file # 將 file 檔案中的帶有 remcarpediem 關鍵詞的行。
grep -C10 remcarpediem file # 將 file 檔案中的帶有 remcarpediem 關鍵詞前後10行的內容。
cat LOG.* | grep "FROM " | grep "WHERE" > b # 將日誌中的所有帶where條件的sql查詢查找出來
grep -li remcarpediem file # 忽略大小寫,並且打印出檔名稱
複製程式碼

現在大家在回頭看一下這段 bash 指令碼,是不是大致瞭解它執行的過程和原理啦。

files=`find services -name "*.java" -or -name "*.js" | xargs grep -il $keyword` 
複製程式碼

後記

本文簡單介紹了程式設計師日常工作中可能用到 Linux 命令的三個場景。大家可以根據自己的實際情況,來判斷是否需要繼續全面詳細地學習相關的知識。畢竟只有能運用於實踐,給自己工作產生價值的技術才是真技術。學習一項技術,就要堅持學以致用的目的。

個人部落格地址,歡迎檢視