一、gawk程序的基本格式
gawk options program file
(1)常用的選項如下:
-F fs 指定行中劃分數據字段的字段分隔符
-f file 從指定文件中讀取程序
-v var=value 定義gawk程序中的一個變量及其默認值
-mf N 指定要處理的數據文件中的最大字段數
-mr N 指定數據文件中的最大數據行數
-W keyword 指定gawk的兼容模式或警告等級
(2)使用的數據字段變量
默認情況下,gawk會將如下變量分配給它在文本行中發現的數據字段:
$0 代表整個文本行
$1 代表文本行中的第一個數據字段
$2 代表文本行中的第二個數據字段
$n 代表文本行中的第n個數據字段
在文本行中,每個數據字段是通過字段分隔符劃分的,gawk默認的字段分隔符是任意的空白字符,例如空格或製表符。
(3)在程序腳本中使用多個命令
gawk程序允許將多條命令組合成一個正常的程序,命令之間是用分號即可。
echo "my name is bob"|gawk '{$4="jack" ; print $0}'
[root@test ~]# echo "my name is bob"|gawk '{$4="jack" ; print $0}'
my name is jack
gawk程序中的第一條命令會給字段變量$4賦值,第二條命令打印整個數據字段。
(4)從文件中讀取程序
gawk編輯器允許將程序存儲到文件中,然後再在命令行中引用。
cat script.gawk
{print $1 "'s home directory is " $6}
gawk -F ':' -f script.gawk /etc/passwd
[root@test ~]# gawk -F ':' -f script.gawk /etc/passwd
root's home directory is /root
bin's home directory is /bin
daemon's home directory is /sbin
可以在程序文件中指定多條命令,只要每條命令放一行即可,不需要用分號。
cat script.gawk
{
text="'s home directory is "
print $1 text $6
}
gawk -F ':' -f script.gawk /etc/passwd
[root@test ~]# gawk -F ':' -f script.gawk /etc/passwd
root's home directory is /root
bin's home directory is /bin
daemon's home directory is /sbin
可以看到gawk程序在引用變量值時並未像shell腳本一樣使用美元符。
(5)在處理數據前運行腳本
gawk在讀取數據前會執行BEGIN關鍵字後指定的程序腳本。
gawk 'BEGIN {print "hello word!"}'
[root@test ~]# gawk 'BEGIN {print "hello word!"}'
hello word!
可以看到print命令會在讀取數據前顯示文本,但是在它顯示了文本後,會快速退出,不等待任何數據。如果想使用正常的程序腳本處理數據,必須使用另一個腳本區域來定義程序。
cat data.txt
line 1
line 2
line 3
gawk 'BEGIN {print "the data file content:"} {print $0}' data.txt
[root@test ~]# gawk 'BEGIN {print "the data file content:"} {print $0}' data.txt
the data file content:
line 1
line 2
line 3
(6)在處理數據後運行腳本
與BEGIN類似,END關鍵字允許指定一個程序腳本,gawk會在讀完數據後執行它。
gawk 'BEGIN {print "the data file content:"} {print $0} END {print "end of file"} ' data.txt
[root@test ~]# gawk 'BEGIN {print "the data file content:"} {print $0} END {print "end of file"} ' data.txt
the data file content:
line 1
line 2
line 3
end of file
(7)綜合小案例
cat script.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 ths listing"
}
gawk -f script.gawk /etc/passwd
[root@test ~]# gawk -f script.gawk /etc/passwd
the latest list of users and shells
UserID Shell
------ -------
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
this concludes ths listing
二、gawk程序的進階使用
(1)使用變量,gawk支持內建變量和自定義變量的使用。
內建變量:
變量 | 描述 |
FIELDWIDTHS | 由空格分隔的一列數字,定義了每個數據字段確切寬度 |
FS | 輸入字段分隔符 |
RS | 輸入記錄分隔符 |
OFS | 輸出字段分隔符 |
ORS | 輸出記錄分隔符 |
變量FS和OFS定義了gawk如何處理數據流中的數據字段。變量OFS具備同樣的功能,只不過是用在print命令的輸出上。
默認情況下,OFS設成一個空格,所以在使用命令:print $1,$2,$3會看到如下的輸出結果
cat data.txt
data11,data12,data13
data21,data22,data23
data31,data32,data33
[root@test ~]# gawk 'BEGIN {FS=","} {print $1,$2,$3}' data.txt
data11 data12 data13
data21 data22 data23
data31 data32 data33
print命令會自動將OFS變量的值放置在輸出中的每個字段之間,通過設置OFS變量,可以在輸出中使用任意字符串來分隔字段。
[root@~]# cat data.txt
data11,data12,data13
data21,data22,data23
data31,data32,data33
[root@~]# gawk 'BEGIN {FS=",";OFS="-"} {print $1,$2,$3}' data.txt
data11-data12-data13
data21-data22-data23
data31-data32-data33
[root@~]# gawk 'BEGIN {FS=",";OFS="<->"} {print $1,$2,$3}' data.txt
data11<->data12<->data13
data21<->data22<->data23
data31<->data32<->data33
FIELDWIDTHS變量允許你不依靠字段分隔符來讀取記錄,在一些應用程序中,數據並沒有使用字段分隔符,而是被放置在了記錄中的特定列,這種情況下,必須設置FIELDWIDTHS變量來匹配數據在記錄中的位置。一旦設置了FIELDWIDTHS變量,gawk就會忽略FS變量,並根據提供的字段寬度來計算字段。
cat data1.txt
1005.3243545.34
117-2.456456.03
06458.3473749.1
[root@~]# gawk 'BEGIN {FIELDWIDTHS="3 5 2 5"} {print $1,$2,$3,$4}' data1.txt
100 5.324 35 45.34
117 -2.45 64 56.03
064 58.34 73 749.1
可以看到上面的例子中使用變量FIELDWIDTHS變量定義了4個字段,gawk依此來解析數據記錄,每個記錄中的數字串會根據以定義好的字段長度來分割。
變量RS和ORS定義了gawk程序如何處理數據流中的記錄,默認情況下,gawk程序將RS和ORS設置爲換行符。默認的RS值表明,輸入數據流中的每行新文本就是一條新紀錄。
cat data2.txt
riley mullen
123 main street
chicago,IL 38383
(312)555-1234
frank will
456 oak street
indianapolis,in 345
(317)555-9876
gawk 'BEGIN {FS="\n";RS=""} {print $1,$4}' data2.txt
[root@test~]# gawk 'BEGIN {FS="\n";RS=""} {print $1,$4}' data2.txt
riley mullen (312)555-1234
frank will (317)555-9876
從上例中可以看到gawk程序把文件中的每行當作一個字段,把空白行當作記錄分隔符。
除了字段和記錄分隔符變量外,gawk還提供了其他的內建變量如下所示:(列舉了幾個常用的)
NF 數據文件中的字段總數
NR 已處理的輸入記錄數
FNR 當前數據文件中的數據行數
當要在gawk程序中跟蹤數據字段和記錄時,變量FNR、NR和NF用着就會很方便。NF變量可以讓你在不知道具體位置的情況下就可以指定記錄中的最後一個數據字段。
gawk 'BEGIN{FS=":";OFS=":"} {print $1,$NF}' /etc/passwd
[root@test~]# gawk 'BEGIN{FS=":";OFS=":"} {print $1,$NF}' /etc/passwd
root:/bin/bash
bin:/sbin/nologin
[root@test]# cat data.txt
data11,data12,data13
data21,data22,data23
data31,data32,data33
gawk 'BEGIN {FS=","} {print $1,"FNR="FNR,"NR="NR} END {print "there where",NR,"records processed"}' data.txt
[root@test]# gawk 'BEGIN {FS=","} {print $1,"FNR="FNR,"NR="NR} END {print "there where",NR,"records processed"}' data.txt
data11 FNR=1 NR=1
data21 FNR=2 NR=2
data31 FNR=3 NR=3
there where 3 records processed
gawk 'BEGIN {FS=","} {print $1,"FNR="FNR,"NR="NR} END {print "there where",NR,"records processed"}' data.txt data.txt
[root@test]# gawk 'BEGIN {FS=","} {print $1,"FNR="FNR,"NR="NR} END {print "there where",NR,"records processed"}' data.txt data.txt
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 where 6 records processed
從結果可以看到,FNR變量的值在gawk處理第二個文件時被重置了,但是NR變量則在處理第二個文件時繼續計數。
結果就是,如果只使用第一個文件作爲輸入,FNR和NR的值相同,如果使用多個文件作爲輸入,FNR的值只會在處理
每個文件時被重置,NR的值則會繼續計數直到處理完所有的數據文件。
自定義變量:
gawk自定義變量名可以是任意數目的字母,下劃線和數字,但不能以數字開頭。重要的是,要記住gawk變量名區分大小寫。
- 在腳本中給變量賦值
gawk變量可以保存數值或文本值。
gawk 'BEGIN{test="this is a test";print test}'
[root@test]# gawk 'BEGIN{text="this is a test";print text}'
this is a test
[root@test]# gawk 'BEGIN{text="this is a test";print text;text=100;print text}'
this is a test
100
賦值語句還可以包含數學算式來處理數字值。
gawk 'BEGIN{x=4;x=x+4;print x}'
[root@test]# gawk 'BEGIN{x=4;x=x+4;print x}'
8
- 在命令行上給變量賦值
[root@test]# cat data.txt
data11,data12,data13
data21,data22,data23
data31,data32,data33
cat script1
BEGIN {FS=","}
{print $n}
gawk -f script1 n=2 data.txt
[root@test]# gawk -f script1 n=2 data.txt
data12
data22
data32
但是使用命令行參數定義變量值會有一個問題,就是設置了變量後,這個值在代碼的BEGIN部分不可用。
[root@test]# cat data.txt
data11,data12,data13
data21,data22,data23
data31,data32,data33
cat script
BEGIN {print "the starting value is",n;FS=","}
{print $n}
gawk -f script n=2 data.txt
[root@test]# gawk -f script n=2 data.txt
the starting value is
data12
data22
data32
可以使用-v參數來解決這個問題,-v參數允許在BEGIN代碼之前設定變量。在命令行上,-v命令參數必須放在腳本代碼之前。
[root@test]# cat data.txt
data11,data12,data13
data21,data22,data23
data31,data32,data33
cat script
BEGIN {print "the starting value is",n;FS=","}
{print $n}
gawk -v n=2 -f script data.txt
[root@test]# gawk -v n=2 -f script data.txt
the starting value is 2
data12
data22
data32
(2)使用模式
- 正則表達式
在使用正則表達式時,正則表達式必須出現在它要控制的程序腳本的左花括號前。
gawk 'BEGIN {FS=","} /11/{print $1}' data.txt
[root@test]# gawk 'BEGIN {FS=","} /11/{print $1}' data.txt
data11
正則表達式/11/匹配了數據字段中含有字符串11的記錄,gawk程序會用正則表達式對記錄中所有的數據字段進行匹配,包括字段分隔符。
- 匹配操作符
匹配操作符允許將正則表達式限定在記錄中的特定數據字段,匹配操作符是波浪線~。可以指定匹配操作符、數據字段變量以及要匹配的正則表達式。
[root@test]# gawk 'BEGIN {FS=","} $2 ~ /^data2/{print $0}' data.txt
data21,data22,data23
匹配操作符會用正則表達式/^data2/來比較第二個數據字段,該正則表達式指明字符串要以文本data2開頭。
gawk -F ':' '$1 ~ /root/{print $1,$NF}' /etc/passwd
[root@test]# gawk -F ':' '$1 ~ /root/{print $1,$NF}' /etc/passwd
root /bin/bash
這個例子會在第一個字段中查找文本root,如果在記錄中找到了這個模式,他會打印該記錄的第一個數據字段和最後一個數據字段值。
也可以使用!排除正則表達式的匹配。
gawk -F ':' '$1 !~ /root/{print $1,$NF}' /etc/passwd
[root@test]# gawk -F ':' '$1 !~ /root/{print $1,$NF}' /etc/passwd
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
shutdown /sbin/shutdown
halt /sbin/halt
- 數學表達式
可以在匹配模式中使用數學表達式,可以使用任何常見的數學表達式。
x==y
x<=y
x<y
x>=y
x>y
顯示所有屬於root用戶組(組ID=0)的系統用戶:
gawk -F ':' '$4 == 0 {print $1}' /etc/passwd
[root@test]# gawk -F ':' '$4 == 0 {print $1}' /etc/passwd
root
sync
shutdown
halt
operator
sys_admin
也可以對文本數據使用表達式,但必須小心,跟正則表達式不同,表達式必須完全匹配,數據必須跟模式嚴格匹配。
[root@test]# cat data.txt
data11,data12,data13
data21,data22,data23
data31,data32,data33
gawk -F ',' '$1 == "data" {print $0}' data.txt
[root@test]# gawk -F ',' '$1 == "data" {print $0}' data.txt
[root@test]# gawk -F ',' '$1 == "data11" {print $0}' data.txt
data11,data12,data13
由實例結果可以看到,第一個測試沒有匹配任何記錄,因爲第一個數據字段值不在任何記錄中,第二個測試用值data11匹配了一條記錄。
(3)格式化打印
print 語句在gawk如何顯示數據上並未提供多少控制。你能做的只是控制輸出字段分隔符( OFS )。格式化打印命令,叫作 printf 。如果你熟悉C語言編程的話,gawk中的printf 命令用法也是一樣,允許指定具體如何顯示數據的指令。
printf 命令的格式:
printf "format string", var1, var2 . . .
format string 是格式化輸出的關鍵。它會用文本元素和格式化指定符來具體指定如何呈現格式化輸出。格式化指定符是一種特殊的代碼,會指明顯示什麼類型的變量以及如何顯示。gawk程序會將每個格式化指定符作爲佔位符,供命令中的變量使用。第一個格式化指定符對應列出的第一個變量,第二個對應第二個變量,依此類推。格式化指定符采用如下格式:
%[modifier]control-letter
其中 control-letter 是一個單字符代碼,用於指明顯示什麼類型的數據,而 modifier 則定義了可選的格式化特性。
控制字母 描 述
c 將一個數作爲ASCII字符顯示
d 顯示一個整數值
i 顯示一個整數值(跟d一樣)
e 用科學計數法顯示一個數
f 顯示一個浮點值
g 用科學計數法或浮點數顯示(選擇較短的格式)
o 顯示一個八進制值
s 顯示一個文本字符串
x 顯示一個十六進制值
X 顯示一個十六進制值,但用大寫字母A~F
因此,如果你需要顯示一個字符串變量,可以用格式化指定符 %s 。如果你需要顯示一個整數值,可以用 %d 或 %i ( %d 是十進制數的C風格顯示方式)。如果你要用科學計數法顯示很大的值,就用 %e 格式化指定符。
gawk 'BEGIN{x = 10 * 100 ;printf "The answer is: %d\n", x}'
[root@test]# gawk 'BEGIN{x = 10 * 100 ;printf "The answer is: %d\n", x}'
The answer is: 1000
除了控制字母外,還有3種修飾符可以用來進一步控制輸出。
width :指定了輸出字段最小寬度的數字值。如果輸出短於這個值, printf會將文本右對齊,並用空格進行填充。如果輸出比指定的寬度還要長,則按照實際的長度輸出。
prec :這是一個數字值,指定了浮點數中小數點後面位數,或者文本字符串中顯示的最大字符數。
-(減號):指明在向格式化空間中放入數據時採用左對齊而不是右對齊。
[root@test]# cat data2.txt
riley mullen
123 main street
chicago,IL 38383
(312)555-1234
frank will
456 oak street
indianapolis,in 345
(317)555-9876
gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2.txt
[root@test]# gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2.txt
riley mullen (312)555-1234
frank will (317)555-9876
可以用 printf 命令來幫助格式化輸出,使得輸出信息看起來更美觀。首先,讓我們將 print命令轉換成 printf 命令,看看會怎樣。
gawk 'BEGIN{FS="\n"; RS=""} {printf "%s %s\n", $1, $4}' data2.txt
[root@test]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%s %s\n", $1, $4}' data2.txt
riley mullen (312)555-1234
frank will (317)555-9876
它會產生跟 print 命令相同的輸出。 printf 命令用 %s 格式化指定符來作爲這兩個字符串值的佔位符。注意,你需要在 printf 命令的末尾手動添加換行符來生成新行。沒添加的話, printf 命令會繼續在同一行打印後續輸出。
printf 命令在處理浮點值時也非常方便。通過爲變量指定一個格式,你可以讓輸出看起來更統一。下面的例子中使用 %5.1f 格式指定符來強制 printf 命令將浮點值近似到小數點後一位。
cat test.txt
130 120 135
110 879 675
234 567 356
gawk '{
total = 0
for (i = 1; i < 4; i++)
{
total += $i
}
avg = total / 3
printf "Average: %5.1f\n",avg
}' test.txt
[root@test]# gawk '{
> total = 0
> for (i = 1; i < 4; i++)
> {
> total += $i
> }
> avg = total / 3
> printf "Average: %5.1f\n",avg
> }' test.txt
Average: 128.3
Average: 554.7
Average: 385.7
通過添加一個值爲 16 的修飾符,我們強制第一個字符串的輸出寬度爲16個字符。默認情況下,printf 命令使用右對齊來將數據放到格式化空間中。要改成左對齊,只需給修飾符加一個減號即可。
[root@test]# cat data2.txt
riley mullen
123 main street
chicago,IL 38383
(312)555-1234
frank will
456 oak street
indianapolis,in 345
(317)555-9876
gawk 'BEGIN{FS="\n"; RS=""} {printf "%16s %s\n", $1,$4}' data2.txt
[root@test]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%16s %s\n", $1,$4}' data2.txt
riley mullen (312)555-1234
frank will (317)555-9876
gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1,$4}' data2.txt
[root@test]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1,$4}' data2.txt
riley mullen (312)555-1234
frank will (317)555-9876