1. 程式人生 > >如何編寫一個優雅的Shell指令碼(三)

如何編寫一個優雅的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的幫助文件。