標籤:style 使用 os io strong 檔案 資料 ar
1.awk簡介
awk是一種程式設計語言,用於在linux/unix下對文本和資料進行處理。資料可以來自標準輸入、一個或多個檔案,或其它命令的輸出。它支援使用者自訂函數和動態Regex等先進功能,是linux/unix下的一個強大編程工具。它在命令列中使用,但更多是作為指令碼來使用。awk的處理文本和資料的方式是這樣的,它漸進式掃描檔案,從第一行到最後一行,尋找匹配的特定模式的行,並在這些行上進行你想要的操作。如果沒有指定處理動作,則把匹配的行顯示到標準輸出(螢幕),如果沒有指定模式,則所有被操作所指定的行都被處理。awk分別代表其作者姓氏的第一個字母。因為它的作者是三個人,分別是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,它提供了Bell實驗室和GNU的一些擴充。下面介紹的awk是以GUN的gawk為例的,在linux系統中已把awk連結到gawk,所以下面全部以awk進行介紹。
2.awk命令格式和選項
2.1. awk的文法有兩種形式
• awk [options] ‘script‘ var=value file(s)
• awk [options] -f scriptfile var=value file(s)
基本用法:
1)在Shell命令列輸入命令調用awk:
awk [-F 域分隔字元] ‘awk程式段’ 輸入檔案
2)將awk程式段插入指令檔後,然後通過awk命令調用它:
awk -f awk指令檔 輸入檔案 【指令檔的首行不以#!/bin/awk –f開頭】
3)將awk命令插入指令檔後,設定該指令檔為可執行,然後直接執行該指令檔,格式為:
./awk指令檔 輸入檔案 【指令檔的首行以#!/bin/awk –f開頭】
awk ‘
BEGIN { actions }
/pattern/ { actions }
/pattern/ { actions }
END { actions }
‘files
2.2. 命令選項
-F fs or --field-separator fs |
指定輸入檔案折分隔字元,fs是一個字串或者是一個Regex,如-F:。 |
-v var=value or --asign var=value |
賦值一個使用者定義變數。 |
-F scripfile or --file scriptfile |
從指令檔中讀取awk命令。 |
-mf nnn and -mr nnn |
對nnn值設定內在限制,-mf選項限制分配給nnn的最大塊數目;-mr選項限制記錄的最大數目。這兩個功能是Bell實驗室版awk的擴充功能,在標準awk中不適用。 |
-W compact or --compat, -W traditional or --traditional |
在相容模式下運行awk。所以gawk的行為和標準的awk完全一樣,所有的awk擴充都被忽略。 |
-W copyleft or --copyleft, -W copyright or --copyright |
列印簡短的著作權資訊。 |
-W help or --help, -W usage or --usage |
列印全部awk選項和每個選項的簡短說明。 |
-W lint or --lint |
列印不能向傳統unix平台移植的結構的警告。 |
-W lint-old or --lint-old |
列印關於不能向傳統unix平台移植的結構的警告。 |
-W posix |
開啟相容模式。但有以下限制,不識別:\x、函數關鍵字、func、換碼序列以及當fs是一個空格時,將新行作為一個域分隔字元;操作符**和**=不能代替^和^=;fflush無效。 |
-W re-interval or --re-inerval |
允許間隔Regex的使用,參考(grep中的Posix字元類),如括號運算式[[:alpha:]]。 |
-W source program-text or --source program-text |
使用program-text作為原始碼,可與-f命令混用。 |
-W version or --version |
列印bug報告資訊的版本。 |
3. 模式和操作
awk指令碼是由模式和操作組成的:
pattern {action} 如$ awk ‘/root/‘ test,或$ awk ‘$3 < 100‘ test。
兩者是可選的,如果沒有模式,則action應用到全部記錄,如果沒有action,則輸出匹配全部記錄。預設情況下,每一個輸入行都是一條記錄,但使用者可通過RS變數指定不同的分隔字元進行分隔。
3.1. 模式
模式可以是以下任意一個:
• /Regex/:使用萬用字元的擴充集。
• 關聯運算式:可以用下面運算子表中的關係運算子進行操作,可以是字串或數位比較,如$2>$1選擇第二個欄位比第一個欄位長的行。
• 模式比對運算式:用運算子~(匹配)和~!(不匹配)。
• 模式,模式:指定一個行的範圍。該文法不能包括BEGIN和END模式。
• BEGIN:讓使用者指定在第一條輸入記錄被處理之前所發生的動作,通常可在這裡設定全域變數。
• END:讓使用者在最後一條輸入記錄被讀取之後發生的動作。
3.2. 操作
操作由一個或多個命令、函數、運算式組成,之間由分行符號或分號隔開,並位於大括弧內。主要有四部份:
• 變數或數組賦值
• 輸出命令
• 內建函數
• 控制流程命令
4. awk的環境變數
Table 1. awk的環境變數
變數 |
描述 |
$n |
目前記錄的第n個欄位,欄位間由FS分隔。 |
$0 |
完整的輸入記錄。 |
ARGC |
命令列參數的數目。 |
ARGIND |
命令列中當前檔案的位置(從0開始算)。 |
ARGV |
包含命令列參數的數組。 |
CONVFMT |
數字轉換格式(預設值為%.6g) |
ENVIRON |
環境變數關聯陣列。 |
ERRNO |
最後一個系統錯誤的描述。 |
FIELDWIDTHS |
欄位寬度列表(用空格鍵分隔)。 |
FILENAME |
當前檔案名稱。 |
FNR |
同NR,但相對於當前檔案。 |
FS |
欄位分隔符號(預設是任何空格)。 |
IGNORECASE |
如果為真,則進行忽略大小寫匹配。 |
NF |
目前記錄中的欄位數。 |
NR |
目前記錄數。 |
OFMT |
數位輸出格式(預設值是%.6g)。 |
OFS |
輸出欄位分隔符號(預設值是一個空格)。 |
ORS |
輸出記錄分隔字元(預設值是一個分行符號)。 |
RLENGTH |
由match函數所匹配的字串的長度。 |
RS |
記錄分隔字元(預設是一個分行符號)。 |
RSTART |
由match函數所匹配的字串的第一個位置。 |
SUBSEP |
數組下標分隔字元(預設值是\034)。 |
5. awk運算子
Table 2. 運算子
運算子 |
描述 |
= += -= *= /= %= ^= **= |
賦值 |
?: |
C條件運算式 |
|| |
邏輯或 |
&& |
邏輯與 |
~ ~! |
匹配Regex和不匹配Regex |
< <= > >= != == |
關係運算子 |
空格 |
串連 |
+ - |
加,減 |
* / & |
乘,除與求餘 |
+ - ! |
一元加,減和邏輯非 |
^ *** |
求冪 |
++ -- |
增加或減少,作為首碼或尾碼 |
$ |
欄位引用 |
in |
數群組成員 |
6. 記錄和域
6.1. 記錄
awk把每一個以分行符號結束的行稱為一個記錄。
記錄分隔字元:預設的輸入和輸出的分隔字元都是斷行符號,儲存在內建變數ORS和RS中。
$0變數:它指的是整條記錄。如$ awk ‘{print $0}‘ test將輸出test檔案中的所有記錄。
變數NR:一個計數器,每處理完一條記錄,NR的值就增加1。如$ awk ‘{print NR,$0}‘ test將輸出test檔案中所有記錄,並在記錄前顯示記錄號。
6.2. 域
記錄中每個單詞稱做“域”,預設情況下以空格或tab分隔。awk可跟蹤域的個數,並在內建變數NF中儲存該值。如$ awk ‘{print $1,$3}‘ test將列印test檔案中第一和第三個以空格分開的列(域)。
6.3. 域分隔字元
內建變數FS儲存輸入欄位分隔字元的值,預設是空格或tab。我們可以通過-F命令列選項修改FS的值。如$ awk -F: ‘{print $1,$5}‘ test將列印以冒號為分隔字元的第一,第五列的內容。
可以同時使用多個域分隔字元,這時應該把分隔字元寫成放到方括弧中,如$awk -F‘[:\t]‘ ‘{print $1,$3}‘ test,表示以空格、冒號和tab作為分隔字元。
輸出域的分隔字元OFS預設是一個空格。如$ awk -F: ‘{print $1,$5}‘ test,$1和$5間的逗號就是OFS的值。
7. gawk專用Regex元字元
一般通用的元字元集就不講了,可參考我的Sed和Grep學習筆記。以下幾個是gawk專用的,不適合unix版本的awk。
\Y |
匹配一個單詞開頭或者末尾的Null 字元串。 |
\B |
匹配單詞內的Null 字元串。 |
\< |
匹配一個單詞的開頭的Null 字元串,錨定開始。 |
\> |
匹配一個單詞的末尾的Null 字元串,錨定末尾。 |
\w |
匹配一個字母數字組成的單詞。 |
\W |
匹配一個非字母數字組成的單詞。 |
\‘ |
匹配字串開頭的一個Null 字元串。 |
\‘ |
匹配字串末尾的一個Null 字元串。 |
8. POSIX字元集
(省略)
9. 匹配操作符(~)
用來在記錄或者域內匹配Regex。如$ awk ‘$1 ~/^root/‘ test將顯示test檔案第一列中以root開頭的行。
10. 比較運算式
conditional expression1 ? expression2: expression3,例如:$ awk ‘{max = {$1 > $3} ? $1: $3: print max}‘ test。如果第一個域大於第三個域,$1就賦值給max,否則$3就賦值給max。
$ awk ‘$1 + $2 < 100‘ test。如果第一和第二個域相加大於100,則列印這些行。
$ awk ‘$1 > 5 && $2 < 10‘ test,如果第一個域大於5,並且第二個域小於10,則列印這些行。
11. 範圍模板
範圍模板匹配從第一個模板的第一次出現到第二個模板的第一次出現之間所有行。如果有一個模板沒出現,則匹配到開頭或末尾。如$ awk ‘/root/,/mysql/‘ test將顯示root第一次出現到mysql第一次出現之間的所有行。
12. 一個驗證passwd檔案有效性的例子
$ cat /etc/passwd | awk -F: ‘\
NF != 7{\
printf("line %d,does not have 7 fields:%s\n",NR,$0)}\
$1 !~ /[A-Za-z0-9]/{printf("line %d,non alpha and numeric user id:%d: %s\n,NR,$0)}\
$2 == "*" {printf("line %d, no password: %s\n",NR,$0)}‘
cat把結果輸出給awk,awk把域之間的分隔字元設為冒號。
如果域的數量(NF)不等於7,就執行下面的程式。
printf列印字串"line ?? does not have 7 fields",並顯示該條記錄。
如果第一個域沒有包含任何字母和數字,printf列印“no alpha and numeric user id" ,並顯示記錄數和記錄。
如果第二個域是一個星號,就列印字串“no passwd”,緊跟著顯示記錄數和記錄本身。
13. 多個命令執行
直接書寫即可,不必像sed那樣加-e參數.例如
awk ‘/[1-9]\.[0-9][0-9]$/ { print $0, "*"} /0\.[1-9][1-9]/ {print ;}‘ zdd.txt
在價格高於1美元的水果後面加* 以引起注意,這裡包含了兩個模式和動作對
14. 格式化列印
%s參數,用來列印字串,可以指定寬度,不足的補空格,正數表示靠右對齊,負數表示靠左對齊。%3s表示字串寬度為3列,右邊對齊,如果字串實際寬度大於3,那麼取實際寬度。
檔案名稱靠左對齊,大小靠左對齊
ls -l | awk ‘{printf "%-16s%\t%-16s\n", $9, $5;}‘
檔案名稱靠左對齊,大小靠右對齊
ls -l | awk ‘{printf "%-16s%\t%16s\n", $9, $5;}‘
檔案名稱靠右對齊,大小靠左對齊
ls -l | awk ‘{printf "%16s%\t%-16s\n", $9, $5;}‘
檔案名稱靠右對齊,大小靠左對齊
ls -l | awk ‘{printf "%16s%\t%16s\n", $9, $5;}‘
15. 幾個執行個體
$ awk ‘{print $3}‘ test |
截取第三域(列)的內容。 |
$ awk ‘/^(no|so)/‘ test |
列印所有以模式no或so開頭的行。 |
$ awk ‘/^[ns]/{print $1}‘ test |
如果記錄以n或s開頭,就列印這個記錄。 |
$ awk ‘$1 ~ /[0-9][0-9]$/{print $1}‘ test |
如果第一個域以兩個數字結束就列印這個記錄。 |
$ awk ‘$1 == 100 || $2 < 50‘ test |
如果第一個或等於100或者第二個域小於50,則列印該行。 |
$ awk ‘$1 != 10‘ test |
如果第一個域不等於10就列印該行。 |
$ awk ‘/test/{print $1 + 10}‘ test |
如果記錄包含Regextest,則第一個域加10並列印出來。 |
$ awk ‘{print ($1 > 5 ? "ok "$1: "error"$1)}‘ test |
如果第一個域大於5則列印問號後面的運算式值,否則列印冒號後面的運算式值。 |
$ awk ‘/^root/,/^mysql/‘test |
列印以Regexroot開頭的記錄到以Regexmysql開頭的記錄範圍內的所有記錄。如果找到一個新的Regexroot開頭的記錄,則繼續列印直到下一個以Regexmysql開頭的記錄為止,或到檔案末尾。 |