如何編寫一個優雅的Shell指令碼(三)
如何編寫一個優雅的Shell指令碼(三)
簡介
awk是shell腳本里面文字處理神奇,它雖然沒有像Java、C、C++這些高階語言那樣開發服務型別的應用程式,但是它擅長的是處理檔案。本部落格主要是對自己以往工作中使用awk的一個總結,不會對awk的語法做一個全面介紹,如果像更深入的瞭解AWK,推薦大家一本awk的書籍《awk與sed》。但是會對工作用到的awk一些功能進行介紹。廢話不少說,進入正題吧。
awk語法
awk其實就是將檔案一行一行的遍歷處理,每一行程式碼處理文字中的一行記錄,讀取的順序。
- 按照檔案的引數傳入順序進行讀取
- 每一個檔案都是從第一行到最後以後順序讀取
一個AWK程式的基本組成部分,包括如下
- BEGIN: 相當於Java中類的建構函式,程式啟動第一個執行的塊
- 主體部分:主體部分通過兩個中括號括起來,一個awk程式裡面可以有多箇中括號,多箇中括號括起來會重複便利引數檔案裡面的內容進行處理(這種功能一半用的比較少)。
awk [option] 'BEGIN{
}
[partition]{
}END{
}' 引數檔案列表
awk -f awk指令碼檔案 引數檔案列表
option ,專案中使用較多的選項有 -f -F -v。
-f選項是將awk的程式寫在一個檔案裡面,通過-f引數將檔案裡面的內容當作awk指令碼程式。
$ cat test.data
1,hello,awk
2,hello,shell
$ cat test.awk
BEGIN{ print "begin" }
{
print $0
}
END { print "end" }
$ awk -f test.awk test.data
begin
1,hello,awk
2,hello,shell
end
-F 制定解析檔案的分隔符,可以多個字元。預設分隔符為空格和製表符。也可以用FS內建變數來實現。
$ awk -F"," '{print $2}' test.data
hello
hello
-v,可以將shell腳本里面的變數傳送到awk中。
$ shellVar="awk"
$ awk -F"," -v shellVar=$shellVar '{print shellVar}' test.data
awk
awk
$ awk -F"," -v shellVar=$shellVar '$3==shellVar{print $0}' test.data
1,hello,awk
awk內建變數
變數 | 含義 |
---|---|
ARGC | 命令列引數個數 |
ARGV | 命令列引數陣列 |
FILENAME | 當前輸入檔名 |
FNR | 當前檔案中的記錄號 |
FS | 輸入域分隔符,預設為一個空格 |
RS | 輸入記錄分隔符 |
NF | 當前記錄裡域個數 |
NR | 到目前為止記錄數 |
OFS | 輸出域分隔符 |
ORS | 輸出記錄分隔符 |
awk內建函式
- split( String, A, [Ere] )
將 String 引數指定的引數分割為陣列元素 A[1], A[2], . . ., A[n],並返回 n 變數的值。此分隔可以通過 Ere 引數指定的擴充套件正則表示式進行,或用當前欄位分隔符(FS 特殊變數)來進行(如果沒有給出 Ere 引數)。除非上下文指明特定的元素還應具有一個數字值,否則 A 陣列中的元素用字串值來建立。
$ awk '{
split($0,a,",");
print a[1],a[2],a[3]
}' test.data
1 hello awk
2 hello shell
# 以上輸出陣列a,split返回的陣列下標以1開始。我們也可以通過OFS變數制定輸出分隔符
$ awk 'BEGIN {OFS="~"}{
split($0,a,",");
print a[1],a[2],a[3]
}' test.data
1~hello~awk
2~hello~shell
-
gsub( Ere, Repl, [ In ] )
除了正則表示式所有具體值被替代這點,它和 sub 函式完全一樣地執行。 -
sub( Ere, Repl, [ In ] )
用 Repl 引數指定的字串替換 In 引數指定的字串中的由 Ere 引數指定的擴充套件正則表示式的第一個具體值。sub 函式返回替換的數量。出現在 Repl 引數指定的字串中的 &(和符號)由 In 引數指定的與 Ere 引數的指定的擴充套件正則表示式匹配的字串替換。如果未指定 In 引數,預設值是整個記錄($0 記錄變數)。
$ awk -F"," '{a=$3; gsub("awk","我是被替換的", $3); print a, $3}' test.data
awk 我是被替換的
shell shell
- match( String, Ere )
在 String 引數指定的字串(Ere 引數指定的擴充套件正則表示式出現在其中)中返回位置(字元形式),從 1 開始編號,或如果 Ere 引數不出現,則返回 0(零)。RSTART 特殊變數設定為返回值。RLENGTH 特殊變數設定為匹配的字串的長度,或如果未找到任何匹配,則設定為 -1(負一)。
$ awk -F"," '{match($3,'/^[a-z]{2}k$/'); print RSTART, RLENGTH}' test.data
1 3
0 -1
# 可以用這個函式,通過正則表示式去匹配字串裡面的子字串
$ awk '{
value=$1
while(match(value,/\w+,/) > 0) {
print substr(value,RSTART,RLENGTH-1);
value=substr(value,RLENGTH+1);
} print value
}' test.data
1
hello
awk
2
hello
shell
- tolower( String )
返回 String 引數指定的字串,字串中每個大寫字元將更改為小寫。大寫和小寫的對映由當前語言環境的 LC_CTYPE 範疇定義。 - toupper( String )
返回 String 引數指定的字串,字串中每個小寫字元將更改為大寫。大寫和小寫的對映由當前語言環境的 LC_CTYPE 範疇定義。
awk實踐
awk檔案關聯
awk可以像SQL一樣,實現兩個和多個檔案關聯。
有如下兩個檔案
$ cat test2.data
1,測試關聯
$ cat test.data
1,hello,awk
2,hello,shell
# test.data 左關聯test2.data
$ awk -F"," '{if (FILENAME=="test2.data") { a[$1]=$2 } else {
print $0, a[$1]
}}' test2.data test.data
1,hello,awk 測試關聯
2,hello,shell
awk檔案拆分
現實工作中,有這樣一個需求,一個介面檔案中,每行記錄有1個或多個欄位包含了一個數組,我們需要將陣列進行拆分成一條條的記錄。
$ cat test3.data
1,test1,陣列11&陣列12
2,test2,陣列21&陣列22
# 拆分後的結果
$ awk -F"," '{
split($3,a,"&"); # 將第三個欄位進行拆分到a陣列
for (i in a) {
print $1,$2,a[i]
}
}' test3.data
1 test1 陣列11
1 test1 陣列12
2 test2 陣列21
2 test2 陣列22
總結
本文主要是針對過往一些工作中使用到shell腳本里面的awk命令進行總結,總結的內容屬於awk的冰山一角。如果awk吸引到你,你可以去系統的瞭解以下awk。建議看看《awk與sed》這本書,或者再linux系統中使用 man命令獲取awk的幫助文件。