shell脚本编程笔记(十二)—— gawk程序
一、 簡介
雖然sed是非常方便自動修改文本文件的工具,但其也有自身的限制。通常你需要一個用來處理文件中的數據的更高級工具,它能提供一個類編程環境來修改和重新組織文件中的數據,這正是gawk能夠做到的。
gawk程序是Unix中的原始awk程序的GNU版本,它讓流編輯邁上了一個新的臺階,提供了一種編程語言而不只是編輯器命令。
在gawk中,你可以做下面的事情:
- 定義變量來保存數據;
- 使用算術和字符串操作符來處理數據;
- 使用結構化編程概念(比如if-then語句和循環)來為數據處理增加處理邏輯;
- 通過提取數據文件中的數據元素,將其重新排列或格式化,生成格式化報告。
gawk程序的報告生成能力通常用來從大文本文件中提取數據元素,并將它們格式化成可讀的報告。其中最完美的例子是格式化日志文件。在日志文件中找出錯誤行會很難, gawk程序可以讓你從日志文件中過濾出需要的數據元素,然后你可以將其格式化,使得重要的數據更易于閱讀。
二、 gawk入門
gawk程序的基本格式如下:gawk options program file
命令行選項提供了一個簡單的途徑來定制gawk程序中的功能。gawk的強大之處在于程序腳本。可以寫腳本來讀取文本行的數據,然后處理并顯示數據,創
建任何類型的輸出報告。
1. 從命令行讀取程序腳本
gawk程序腳本用一對花括號來定義。你必須將腳本命令放到{}中,由于gawk命令行假定腳本是單個文本字符串,你還必須將腳本放到單引號中。
gawk '{print "Hello World!"}'如果嘗試運行這個命令,你可能會有些失望,因為什么都不會發生。原因在于沒有在命令行上指定文件名,所以gawk程序會從STDIN接收數據。在運行這個程序時,它會一直等待從STDIN輸入的文本。
如果你輸入一行文本并按下回車鍵, gawk會對這行文本運行一遍程序腳本。跟sed編輯器一樣, gawk程序會針對數據流中的每行文本執行程序腳本。由于程序腳本被設為顯示一行固定的文本字符串,因此不管你在數據流中輸入什么文本,都會得到同樣的文本輸出。
2. 使用數據字段變量
gawk的主要特性之一是其處理文本文件中數據的能力。它會自動給一行中的每個數據元素分配一個變量。默認情況下, gawk會將如下變量分配給它在文本行中發現的數據字段:
- $0代表整個文本行;
- $1代表文本行中的第1個數據字段;
- $2代表文本行中的第2個數據字段;
- $n代表文本行中的第n個數據字段。
在文本行中,每個數據字段都是通過字段分隔符劃分的。 gawk在讀取一行文本時,會用預定義的字段分隔符劃分每個數據字段。 gawk中默認的字段分隔符是任意的空白字符(例如空格或制表符)。
在下面的例子中, gawk程序讀取文本文件,只顯示第1個數據字段的值。
cat data2.txt One line of test text. Two lines of test text. Three lines of test text.gawk '{print $1}' data2.txt One Two Three如果你要讀取采用了其他字段分隔符的文件,可以用-F選項指定。
gawk -F: '{print $1}' /etc/passwd3. 在程序腳本中使用多個命令
gawk允許你將多條命令組合成一個正常的程序。要在命令行上的程序腳本中使用多條命令,只要在命令之間放個分號即可。
echo "My name is Rich" | gawk '{$4="Christine"; print $0}'第一條命令會給字段變量$4賦值。第二條命令會打印整個數據字段。
也可以用次提示符一次一行地輸入程序腳本命令。
$ gawk '{ > $4="Christine" > print $0}' My name is Rich My name is Christine在你用了表示起始的單引號后, bash shell會使用次提示符來提示你輸入更多數據。你可以每次在每行加一條命令,直到輸入了結尾的單引號。因為沒有在命令行中指定文件名,gawk會從STDIN中獲得數據。當運行這個程序的時候,它會等著讀取來自STDIN的文本。
4. 從文件中讀取程序
跟sed一樣, gawk允許將程序存儲到文件中,然后再在命令行中引用。
cat script2.gawk {print $1 "'s home directory is " $6}gawk -F: -f script2.gawk /etc/passwd root's home directory is /root bin's home directory is /bin daemon's home directory is /sbin adm's home directory is /var/adm lp's home directory is /var/spool/lpd [...]可以在程序文件中指定多條命令,只要一條命令放一行即可,不需要用分號。
cat script3.gawk { text = "'s home directory is " print $1 text $6 }gawk -F: -f script3.gawk /etc/passwd root's home directory is /root bin's home directory is /bin daemon's home directory is /sbin adm's home directory is /var/adm lp's home directory is /var/spool/lpd [...]5. 在處理數據前運行腳本
gawk還允許指定程序腳本何時運行。默認情況下,gawk會從輸入中讀取一行文本,然后針對該行的數據執行程序腳本。有時可能需要在處理數據前運行腳本,比如為報告創建標題。BEGIN關鍵字就是用來做這個的。它會強制gawk在讀取數據前執行BEGIN關鍵字后指定的程序腳本。
cat data3.txt Line 1 Line 2 Line 3gawk 'BEGIN {print "The File Contents:"} > {print $0}' data3.txt The data3 File Contents: Line 1 Line 2 Line 3或者
gawk 'BEGIN {print "The File Contents:"};{print $0}' /etc/passwd6. 在處理數據后運行腳本
與BEGIN類似, END關鍵字允許你指定一個程序腳本, gawk會在讀完數據后執行它。
gawk 'BEGIN {print "The data3 File Contents:"} > {print $0} > END {print "End of File"}' data3.txt The data3 File Contents: Line 1# 或者 gawk 'BEGIN {print "The File Contents:"};{print $0};END {print "End of File"}' /etc/passwd當gawk程序打印完文件內容后,它會執行END腳本中的命令。這是在處理完所有正常數據后給報告添加頁腳的最佳方法。
可以將所有這些內容放到一起組成一個漂亮的小程序腳本文件,用它從一個簡單的數據文件中創建一份完整的報告。
cat script4.gawk BEGIN { print "The latest list of users and shells" print " UserID \t Shell" print "-------- \t -------" FS=":" } { print $1 " \t " $7 } END { print "This concludes the listing" }gawk -f script4.gawk /etc/passwd三、?使用變量
所有編程語言共有的一個特性是使用變量來存取值,gawk支持兩種不同類型的變量:
- 內建變量
- 自定義變量
1.?內建變量
gawk使用內建變量來引用程序數據里的一些特殊功能,例如處理數據文件中的數據字段和記錄的信息。
- 字段和記錄分隔符變量
變量FS和OFS定義了gawk如何處理數據流中的數據字段。你已經知道了如何使用變量FS來定義記錄中的字段分隔符。變量OFS具備相同的功能,只不過是用在print命令的輸出上。默認情況下, gawk將OFS設成一個空格
cat data1 data11,data12,data13,data14,data15 data21,data22,data23,data24,data25 data31,data32,data33,data34,data35gawk 'BEGIN{FS=","} {print $1,$2,$3}' data1 data11 data12 data13 data21 data22 data23 data31 data32 data33print命令會自動將OFS變量的值放置在輸出中的每個字段間。 通過設置OFS變量,可以在輸出中使用任意字符串來分隔字段。
gawk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' data1 data11-data12-data13 data21-data22-data23 data31-data32-data33gawk 'BEGIN{FS=","; OFS="<-->"} {print $1,$2,$3}' data1 data11<-->data12<-->data13 data21<-->data22<-->data23 data31<-->data32<-->data33- FIELDWIDTHS變量
FIELDWIDTHS變量允許你不依靠字段分隔符來讀取記錄。在一些應用程序中,數據并沒有使用字段分隔符,而是被放置在了記錄中的特定列。這種情況下,必須設定FIELDWIDTHS變量來匹配數據在記錄中的位置。
一旦設置了FIELDWIDTH變量, gawk就會忽略FS變量,并根據提供的字段寬度來計算字段。
cat data1b 1005.3247596.37 115-2.349194.00 05810.1298100.1gawk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data1b 100 5.324 75 96.37 115 -2.34 91 94.00 058 10.12 98 100.1FIELDWIDTHS變量定義了四個字段, gawk依此來解析數據記錄。每個記錄中的數字串會根據已定義好的字段長度來分割。
- 變量RS和ORS
變量RS和ORS定義了gawk程序如何處理數據流中的字段。默認情況下, gawk將RS和ORS設為換行符。默認的RS值表明,輸入數據流中的每行新文本就是一條新紀錄。
有時,你會在數據流中碰到占據多行的字段。典型的例子是包含地址和電話號碼的數據,其中地址和電話號碼各占一行。
如果你用默認的FS和RS變量值來讀取這組數據, gawk就會把每行作為一條單獨的記錄來讀取,并將記錄中的空格當作字段分隔符。這可不是你希望看到的。
要解決這個問題,只需把FS變量設置成換行符。這就表明數據流中的每行都是一個單獨的字段,每行上的所有數據都屬于同一個字段。但現在令你頭疼的是無從判斷一個新的數據行從何開始。
對于這一問題,可以把RS變量設置成空字符串,然后在數據記錄間留一個空白行。 gawk會把每個空白行當作一個記錄分隔符。
gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2 Riley Mullen (312)555-1234 Frank Williams (317)555-9876 Haley Snell (313)555-4938- 數據變量
除了字段和記錄分隔符變量外,gawk還提供了其他一些內建變量來幫助你了解數據發生了什么變化,并提取shell環境的信息。
- ARGC和ARGV變量
ARGC和ARGV變量允許從shell中獲得命令行參數的總數以及它們的值。但這可能有點麻煩,因為gawk并不會將程序腳本當成命令行參數的一部分。
gawk 'BEGIN{print ARGC,ARGV[1]}' data1 2 data1ARGC變量表明命令行上有兩個參數。這包括gawk命令和data1參數(記住,程序腳本并不算參數)。 ARGV數組從索引0開始,代表的是命令。第一個數組值是gawk命令后的第一個命令行參數。
- ENVIRON變量
ENVIRON變量看起來可能有點陌生。它使用關聯數組來提取shell環境變量。關聯數組用文本作為數組的索引值,而不是數值。數組索引中的文本是shell環境變量名,而數組的值則是shell環境變量的值。
gawk ' > BEGIN{ > print ENVIRON["HOME"] > print ENVIRON["PATH"] > }' /home/rich /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin可以用這種方法來從shell中提取任何環境變量的值,以供gawk程序使用。
當要在gawk程序中跟蹤數據字段和記錄時,變量FNR、 NF和NR用起來就非常方便。有時你并不知道記錄中到底有多少個數據字段。NF變量可以讓你在不知道具體位置的情況下指定記錄中的最后一個數據字段。
gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd rich:/bin/bash testy:/bin/csh mark:/bin/bash dan:/bin/bash mike:/bin/bash test:/bin/bash- FNR變量與NR變量
FNR變量含有當前數據文件中已處理過的記錄數,NR變量則含有已處理過的記錄總數。
gawk ' > BEGIN {FS=","} > {print $1,"FNR="FNR,"NR="NR} > END{print "There were",NR,"records processed"}' data1 data1 data11 FNR=1 NR=1 data21 FNR=2 NR=2 data31 FNR=3 NR=3 data11 FNR=1 NR=4 data21 FNR=2 NR=5 data31 FNR=3 NR=6 There were 6 records processedFNR變量的值在gawk處理第二個數據文件時被重置了,而NR變量則在處理第二個數據文件時繼續計數。結果就是:如果只使用一個數據文件作為輸入, FNR和NR的值是相同的;如果使用多個數據文件作為輸入, FNR的值會在處理每個數據文件時被重置,而NR的值則會繼續計數直到處理完所有的數據文件。
2.?自定義變量
gawk自定義變量名可以是任意數目的字母、數字和下劃線,但不能以數字開頭。重要的是,要記住gawk變量名區分大小寫。
在gawk程序中給變量賦值跟在shell腳本中賦值類似,都用賦值語句,變量可以保存數值或文本值。
gawk ' > BEGIN{ > testing="This is a test" > print testing > testing=45 > print testing > }' This is a test 45賦值語句還可以包含數學算式來處理數字值。
gawk 'BEGIN{x=4; x= x * 2 + 3; print x}' 11如你在這個例子中看到的, gawk編程語言包含了用來處理數字值的標準算數操作符。其中包括求余符號(%)和冪運算符號(^或**)。
3. 在命令行上給變量賦值
也可以用gawk命令行來給程序中的變量賦值。這允許你在正常的代碼之外賦值,即時改變變量的值。下面的例子使用命令行變量來顯示文件中特定數據字段。
cat script1 BEGIN{FS=","} {print $n}gawk -f script1 n=2 data1 data12 data22 data32gawk -f script1 n=3 data1 data13 data23 data33這個特性可以讓你在不改變腳本代碼的情況下就能夠改變腳本的行為。第一個例子顯示了文件的第二個字段,第二個則顯示了第三個字段,只要在命令行上設置n變量的值就行。
使用命令行參數來定義變量值會有一個問題,這個值在代碼的BEGIN部分不可用。
cat script2 BEGIN{print "The starting value is",n; FS=","} {print $n}gawk -f script2 n=3 data1 The starting value is data13 data23 data33可以用-v命令行參數來解決這個問題,它允許你在BEGIN代碼之前設定變量,-v命令行參數必須放在腳本代碼之前。
gawk -v n=3 -f script2 data1 The starting value is 3 data13 data23 data33現在在BEGIN代碼部分中的變量n的值已經是命令行上設定的那個值了。
4.?處理數組
gawk使用關聯數組提供數組功能。關聯數組跟數字數組不同之處在于它的索引值可以是任意文本字符串。每個索引字符串都必須能夠唯一地標識出賦給它的數據元素。如果你熟悉其他編程語言的話,就知道這跟散列表和字典是同一個概念。
?
1)定義數組變量
可以用標準賦值語句來定義數組變量。數組變量賦值的格式如下:var[index] = element
其中var是變量名, index是關聯數組的索引值, element是數據元素值。在引用數組變量時,必須包含索引值來提取相應的數據元素值。
在引用數組變量時,會得到數據元素的值。數據元素值是數字值時也一樣。
gawk 'BEGIN{ > var[1] = 34 > var[2] = 3 > total = var[1] + var[2] > print total > }' 37
2)遍歷數組變量
關聯數組變量的問題在于你可能無法知曉索引值是什么,關聯數組的索引可以是任何東西。如果要在gawk中遍歷一個關聯數組,可以用for語句的一種特殊形式。
for (var in array) { statements }這個for語句會在每次循環時將關聯數組array的下一個索引值賦給變量var,然后執行一
遍statements。重要的是記住這個變量中存儲的是索引值而不是數組元素值。可以將這個變量用作數組的索引,輕松地取出數據元素值。
注意,索引值不會按任何特定順序返回,但它們都能夠指向對應的數據元素值。你不能指望著返回的值都是有固定的順序,只能保證索引值和數據值是對應的。
3)刪除數組變量
從關聯數組中刪除數組索引要用一個特殊的命令??delete array[index]
刪除命令會從數組中刪除關聯索引值和相關的數據元素值。一旦從關聯數組中刪除了索引值,你就沒法再用它來提取元素值。
gawk 'BEGIN{ > var["a"] = 1 > var["g"] = 2 > for (test in var) > { > print "Index:",test," - Value:",var[test] > } > delete var["g"] > print "---" > for (test in var) > print "Index:",test," - Value:",var[test] > }' Index: a - Value: 1 Index: g - Value: 2 --- Index: a - Value: 1
四、使用模式
gawk程序支持多種類型的匹配模式來過濾數據記錄,這一點跟sed編輯器大同小異。
1.?正則表達式
正則表達式必須出現在它要控制的程序腳本的左花括號前。
gawk 'BEGIN{FS=","} /11/{print $1}' data1 data11正則表達式/11/匹配了數據字段中含有字符串11的記錄。 gawk程序會用正則表達式對記錄中所有的數據字段進行匹配,包括字段分隔符。
gawk 'BEGIN{FS=","} /,d/{print $1}' data1 data11 data21 data31這個例子使用正則表達式匹配了用作字段分隔符的逗號。這也并不總是件好事。它可能會造成:當試圖匹配某個數據字段中的特定數據時,這些數據又出現在其他數據字段中。如果需要用正則表達式匹配某個特定的數據實例,應該使用匹配操作符。
2. 匹配操作符
匹配操作符允許將正則表達式限定在記錄中的特定數據字段。匹配操作符是波浪線~。可以指定匹配操作符、數據字段變量以及要匹配的正則表達式。
$1 ~ /^data/$1變量代表記錄中的第一個數據字段。這個表達式會過濾出第一個字段以文本data開頭的所有記錄。這可是件強大的工具, gawk中經常用它在數據文件中搜索特定的數據元素。
gawk -F: '$1 ~ /rich/{print $1,$NF}' /etc/passwd rich /bin/bash你也可以用!符號來排除正則表達式的匹配。
$1 !~ /expression/如果記錄中沒有找到匹配正則表達式的文本,程序腳本就會作用到記錄數據。
gawk –F: '$1 !~ /rich/{print $1,$NF}' /etc/passwd root /bin/bash daemon /bin/sh bin /bin/sh sys /bin/sh --- output truncated ---
3.?數學表達式
除了正則表達式,你也可以在匹配模式中用數學表達式。這個功能在匹配數據字段中的數字值時非常方便。舉個例子,如果你想顯示所有屬于root用戶組(組ID為0)的系統用戶,可以用這個腳本。
gawk -F: '$4 == 0{print $1}' /etc/passwd root sync shutdown halt operator可以使用任何常見的數學比較表達式:==, <= ,< ,>=,>
也可以對文本數據使用表達式,但必須小心。跟正則表達式不同,表達式必須完全匹配。數據必須跟模式嚴格匹配。
$ gawk -F, '$1 == "data"{print $1}' data1 $ $ gawk -F, '$1 == "data11"{print $1}' data1 data11第一個測試沒有匹配任何記錄,因為第一個數據字段的值不在任何記錄中。第二個測試用值data11匹配了一條記錄。
五、?結構化命令
1.?if 語句
if (condition) statement1 # 也可以將它放在一行上,像這樣: if (condition) statement1下面這個簡單的例子演示了這種格式的。
$ cat data4 10 5 13 50 34 $ gawk '{if ($1 > 20) print $1}' data4 50 34如果需要在if語句中執行多條語句,就必須用花括號將它們括起來。gawk的if語句也支持else子句,允許在if語句條件不成立的情況下執行一條或多條語句。
gawk '{ > if ($1 > 20) > { > x = $1 * 2 > print x > } else > { > x = $1 / 2 > print x > }}' data4 5 2.5 6.5 100 68可以在單行上使用else子句,但必須在if語句部分之后使用分號。
if (condition) statement1; else statement2以下是上一個例子的單行格式版本,這個格式更緊湊,但也更難理解
gawk '{if ($1 > 20) print $1 * 2; else print $1 / 2}' data4 5 2.5 6.5 100 682.?while 語句
while語句為gawk程序提供了一個基本的循環功能,下面是while語句的格式。
while (condition) { statements }while循環遍歷一組數據,并檢查迭代的結束條件。如果在計算中必須使用每條記錄中
的多個數據值,這個功能能幫得上忙。
while語句會遍歷記錄中的數據字段,將每個值都加到total變量上,并將計數器變量i增值。當計數器值等于4時, while的條件變成了FALSE,循環結束,然后執行腳本中的下一條語句。這條語句會計算并打印出平均值。這個過程會在數據文件中的每條記錄上不斷重復。
gawk支持在while循環中使用break語句和continue語句,允許你從循環中跳出。
gawk '{ > total = 0 > i = 1 > while (i < 4) > { > total += $i > if (i == 2) > break > i++ > } > avg = total / 2 > print "The average of the first two data elements is:",avg > }' data5 The average of the first two data elements is: 125 The average of the first two data elements is: 136.5 The average of the first two data elements is: 157.5break語句用來在i變量的值為2時從while循環中跳出。
?
3.?do-while 語句
do-while語句類似于while語句,但會在檢查條件語句之前執行命令。
do { statements } while (condition)這種格式保證了語句會在條件被求值之前至少執行一次。當需要在求值條件前執行語句時,這個特性非常方便。
gawk '{ > total = 0 > i = 1 > do > { > total += $i > i++ > } while (total < 150) > print total }' data5 250 160 315
4.?for 語句
for語句是許多編程語言執行循環的常見方法。 gawk編程語言支持C風格的for循環。
for( variable assignment; condition; iteration process) gawk '{ > total = 0 > for (i = 1; i < 4; i++) > { > total += $i > } > avg = total / 3 > print "Average:",avg > }' data5 Average: 128.333 Average: 137.667 Average: 176.667
六、?格式化打印
如果要創建詳盡的報表,通常需要為數據選擇特定的格式和位置。解決辦法是使用格式化打印命令,叫作printf。如果你熟悉C語言編程的話, gawk中的printf命令用法也是一樣,允許指定具體如何顯示數據的指令。
下面是printf命令的格式:
printf "format string", var1, var2 ...format string是格式化輸出的關鍵。它會用文本元素和格式化指定符來具體指定如何呈
現格式化輸出。格式化指定符是一種特殊的代碼,會指明顯示什么類型的變量以及如何顯示。
gawk程序會將每個格式化指定符作為占位符,供命令中的變量使用。第一個格式化指定符對應列出的第一個變量,第二個對應第二個變量,依此類推。格式化指定符采用如下格式
%[modifier]control-letter其中control-letter是一個單字符代碼,用于指明顯示什么類型的數據,而modifier則
定義了可選的格式化特性。
因此,如果你需要顯示一個字符串變量,可以用格式化指定符%s。如果你需要顯示一個整數值,可以用%d或%i(%d是十進制數的C風格顯示方式)。如果你要用科學計數法顯示很大的值,就用%e格式化指定符。
gawk 'BEGIN{ > x = 10 * 100 > printf "The answer is: %e\n", x > }' The answer is: 1.000000e+03除了控制字母外,還有3種修飾符可以用來進一步控制輸出。
- width:指定了輸出字段最小寬度的數字值。如果輸出短于這個值, printf會將文本右對齊,并用空格進行填充。如果輸出比指定的寬度還要長,則按照實際的長度輸出。
- prec:這是一個數字值,指定了浮點數中小數點后面位數,或者文本字符串中顯示的最大字符數。
- -(減號):指明在向格式化空間中放入數據時采用左對齊而不是右對齊。
在使用printf語句時,你可以完全控制輸出樣式。
gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2 Riley Mullen (312)555-1234 Frank Williams (317)555-9876 Haley Snell (313)555-4938可以用printf命令來幫助格式化輸出,使得輸出信息看起來更美觀。首先,讓我們將print
命令轉換成printf命令,看看會怎樣。
它會產生跟print命令相同的輸出。 printf命令用%s格式化指定符來作為這兩個字符串值
的占位符。注意,你需要在printf命令的末尾手動添加換行符來生成新行。沒添加的話, printf命令會繼續在同一行打印后續輸出。
如果需要用幾個單獨的printf命令在同一行上打印多個輸出,這就會非常有用。
gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1 data11 data21 data31每個printf的輸出都會出現在同一行上。為了終止該行, END部分打印了一個換行符。
下一步,用修飾符來格式化第一個字符串值。
通過添加一個值為16的修飾符,我們強制第一個字符串的輸出寬度為16個字符。默認情況下,printf命令使用右對齊來將數據放到格式化空間中。要改成左對齊,只需給修飾符加一個減號即可。
gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1, $4}' data2 Riley Mullen (312)555-1234 Frank Williams (317)555-9876 Haley Snell (313)555-4938printf命令在處理浮點值時也非常方便。通過為變量指定一個格式,你可以讓輸出看起來
更統一。可以使用%5.1f格式指定符來強制printf命令將浮點值近似到小數點后一位。
七、?內建函數
gawk提供了不少內置函數,可進行一些常見的數學、字符串以及時間函數運算。你可以在gawk程序中利用這些函數來減少腳本中的編碼工作。
1. 數學函數
如果你有過其他語言的編程經驗,可能就會很熟悉在代碼中使用內建函數來進行一些常見的數學運算。 gawk編程語言不會讓那些尋求高級數學功能的程序員失望。
雖然數學函數的數量并不多,但gawk提供了標準數學運算中要用到的一些基本元素。
在使用一些數學函數時要小心,因為gawk語言對于它能夠處理的數值有一個限定區間。如果超出了這個區間,就會得到一條錯誤消息。
$ gawk 'BEGIN{x=exp(100); print x}' 26881171418161356094253400435962903554686976 $ gawk 'BEGIN{x=exp(1000); print x}' gawk: warning: exp argument 1000 is out of range inf第一個例子會計算e的100次冪,雖然數值很大,但尚在系統的區間內。第二個例子嘗試計算e的1000次冪,已經超出了系統的數值區間,所以就生成了一條錯誤消息。
除了標準數學函數外, gawk還支持一些按位操作數據的函數,位操作函數在處理數據中的二進制值時非常有用。
- and(v1, v2):執行值v1和v2的按位與運算。
- compl(val):執行val的補運算。
- lshift(val, count):將值val左移count位。
- or(v1, v2):執行值v1和v2的按位或運算。
- rshift(val, count):將值val右移count位。
- xor(v1, v2):執行值v1和v2的按位異或運算。
2.?字符串函數
gawk還提供了一些可用來處理字符串值的函數
一些字符串函數的作用相對來說顯而易見。
gawk 'BEGIN{x = "testing"; print toupper(x); print length(x) }' TESTING 7但一些字符串函數的用法相當復雜。asort和asorti函數是新加入的gawk函數,允許你基
于數據元素值(asort)或索引值(asorti)對數組變量進行排序。
新數組test含有排序后的原數組的數據元素,但索引值現在變為表明正確順序的數字值了。split函數是將數據字段放到數組中以供進一步處理的好辦法。
gawk 'BEGIN{ FS=","}{ > split($0, var) > print var[1], var[5] > }' data1 data11 data15 data21 data25 data31 data35新數組使用連續數字作為數組索引,從含有第一個數據字段的索引值1開始。
3.?時間函數
時間函數常用來處理日志文件,而日志文件則常含有需要進行比較的日期。通過將日期的文本表示形式轉換成epoch時間(自1970-01-01 00:00:00 UTC到現在的秒數),可以輕松地比較日期。
gawk 'BEGIN{ > date = systime() > day = strftime("%A, %B %d, %Y", date) > print day > }' Friday, December 26, 2014該例用systime函數從系統獲取當前的epoch時間戳,然后用strftime函數將它轉換成用戶
可讀的格式,轉換過程中使用了shell命令date的日期格式化字符。
?
4. 自定義函數
除了gawk中的內建函數,還可以在gawk程序中創建自定義函數。
1)定義函數
要定義自己的函數,必須用function關鍵字。
function name([variables]) { statements }函數名必須能夠唯一標識函數。可以在調用的gawk中傳給這個函數一個或多個變量。
function printthird() { print $3 }這個函數會打印記錄中的第三個數據字段。
函數還能用return語句返回值:return value
值可以是變量,或者是最終能計算出值的算式:
function myrand(limit) { return int(limit * rand()) }你可以將函數的返回值賦給gawk程序中的一個變量:x = myrand(100)
2)使用自定義函數
在定義函數時,它必須出現在所有代碼塊之前(包括BEGIN代碼塊)。乍一看可能有點怪異,但它有助于將函數代碼與gawk程序的其他部分分開。
gawk ' > function myprint() > { > printf "%-16s - %s\n", $1, $4 > } > BEGIN{FS="\n"; RS=""} > { > myprint() > }' data2 Riley Mullen - (312)555-1234 Frank Williams - (317)555-9876 Haley Snell - (313)555-4938這個函數定義了myprint()函數,它會格式化記錄中的第一個和第四個數據字段以供打印輸出。 gawk程序然后用該函數顯示出數據文件中的數據。一旦定義了函數,你就能在程序的代碼中隨意使用。在涉及很大的代碼量時,這會省去許多工作。
?
5. 創建函數庫
顯而易見,每次使用函數都要重寫一遍并不美妙。不過, gawk提供了一種途徑來將多個函數放到一個庫文件中,這樣你就能在所有的gawk程序中使用了。
首先,你需要創建一個存儲所有gawk函數的文件。
cat funclib function myprint() { printf "%-16s - %s\n", $1, $4 } function myrand(limit) { return int(limit * rand()) } function printthird() { print $3 }funclib文件含有三個函數定義。需要使用-f命令行參數來使用它們。很遺憾,不能將-f命令行參數和內聯gawk腳本放到一起使用,不過可以在同一個命令行中使用多個-f參數。因此,要使用庫,只要創建一個含有你的gawk程序的文件,然后在命令行上同時指定庫文件
和程序文件就行了。
你要做的是當需要使用庫中定義的函數時,將funclib文件加到你的gawk命令行上就可以了。
八、?實例
如果需要處理數據文件中的數據值,例如表格化銷售數據或者是計算保齡球得分, gawk的一些高級特性就能派上用場。處理數據文件時,關鍵是要先把相關的記錄放在一起,然后對相關數據執行必要的計算。
舉例來說,我們手邊有一個數據文件,其中包含了兩支隊伍(每隊兩名選手)的保齡球比賽
得分情況。
每位選手都有三場比賽的成績,這些成績都保存在數據文件中,每位選手由位于第二列的隊名來標識。下面的腳本對每隊的成績進行了排序,并計算了總分和平均分。
$ cat bowling.sh #!/bin/bash for team in $(gawk –F, '{print $2}' scores.txt | uniq) do gawk –v team=$team 'BEGIN{FS=","; total=0} { if ($2==team) { total += $3 + $4 + $5; } } END { avg = total / 6; print "Total for", team, "is", total, ",the average is",avg }' scores.txt donefor循環中的第一條語句過濾出數據文件中的隊名,然后使用uniq命令返回不重復的隊名。for循環再對每個隊進行迭代。for循環內部的gawk語句進行計算操作。對于每一條記錄,首先確定隊名是否和正在進行循環的隊名相符。這是通過利用gawk的-v選項來實現的,該選項允許我們在gawk程序中傳遞shell變量。如果隊名相符,代碼會對數據記錄中的三場比賽得分求和,然后將每條記錄的值再相加,只要數據記錄屬于同一隊。在循環迭代的結尾處, gawk代碼會顯示出總分以及平均分。輸出結果如下。
./bowling.sh Total for team1 is 635, the average is 105.833 Total for team2 is 706, the average is 117.667現在你就擁有了一件趁手的工具來計算保齡球錦標賽成績了。你要做的就是將每位選手的成績記錄在文本文件中,然后運行這個腳本!
總結
以上是生活随笔為你收集整理的shell脚本编程笔记(十二)—— gawk程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【ZT】2D动画制作工具比较
- 下一篇: ps调色技能