Linux awk命令(一)基礎
awk 是一種程式語言,用於在linux/unix下對文字和資料進行處理,它能提供一個類程式設計環境來修改和組織檔案中的資料。它比sed功能更為強大,可以看看sed的使用:
Linux sed命令(一)基礎
Linux sed命令(二)進階
awk分別代表其作者姓氏的第一個字母,它的作者是三個人,分別是Alfred Aho、Brian Kernighan、Peter Weinberger。gawk是awk的GNU版本,在所有的發行版中預設沒有安裝gawk,我們可以手動安裝。
使用awk,你可以做以下事情:
- 定義變數來儲存資料
- 使用算數和字串操作符來處理資料
- 使用結構化程式設計的概念的邏輯來處理資料
- 通過提取資料檔案中的資料元素,將其重新排列或格式化,生成格式化報告
一、awk命令格式
awk命令格式如下:
awk [options] program_script file
option指定命令列選項,如:
-F fs 指定行中劃分資料欄位的欄位分隔符
-f file 從指定的檔案中讀取awk程式
-v var=value 指定awk程式中的一個變數及其預設值
...
我們會在探索awk時逐漸的使用這些命令列選項。
awk強大之處在於program程式指令碼,可以用指令碼來讀取文字行的資料,然後處理並顯示資料,建立任何型別的報告。awk程式中指令碼使用一對大括號{}來定義,並且由於awk命令列假定指令碼是單個文字字串,你還必須將指令碼放在單引號中,即 '{ program_script}'
資料欄位變數:
awk會將如下變數給配給它在文字行中發現的資料欄位:
$0 代表整個文字行
$1 代表文字行中的第1個欄位
$2 代表文字行中的第2個欄位
...
$n 代表文字行中的第n個欄位
在文字行中,每個欄位都是通過欄位分隔符劃分的,預設的欄位分隔符是空白字元(如空格、製表符)。當然,我們可以通過-F選項修改欄位分隔符。請看示例,我們使用這一行命令awk -F: -f script1.awk /etc/passwd
來輸出使用者名稱及其目錄,注意首先用-F改變了欄位分隔符,然後又用-f指定了指令碼,指令碼中只有一行程式碼 {print $1 "'s home is " $6}
root's home is /root
daemon's home is /usr/sbin
bin's home is /bin
sys's home is /dev
...
BEGIN和END使用:
預設情況下,awk會從輸入中讀取一行文字然後針對這一行的資料執行程式指令碼。有時可能需要在處理資料前或後執行指令碼,比如建立標題和新增頁尾說明等。這時我們就要使用BEGIN和END關鍵字。
# script1.awk
BEGIN {
print "This is the title line"
print "Change the FS"
FS=":"
}
{print $1 "'s home is " $6}
END{
print "This is the end line."
}
# 執行命令
awk -f script1.awk /etc/passwd
如上程式碼所示,我們在腳本里使用了BEGIN和END,並且在BEGIN中指定了分隔符FS。在終端執行命令後,會發現輸出的首末行會有我們在BEGIN和END中寫的提示語。
二、awk中的變數
awk中使用支援兩種不同的型別變數:內建變數和自定義變數。
2.1、內建變數
使用內建變數來引用程式資料裡的一些特殊功能。
1、欄位和記錄分隔符變數
資料欄位(field)是由欄位分隔符(field separator)來劃定的,前面我們提到過。gawk的輸入可以從標準輸入或指定的檔案裡讀取,輸入的讀取單位被稱為”記錄”(records),gawk 在做處理時,是一個記錄一個記錄地處理。每個記錄的預設值是一行(line),一個記錄又被分為多個欄位(fields)。
我們既然可以修改欄位分隔符(field separator)來修改欄位的讀取方式,那麼也可以修改記錄分隔符(record separator)來分隔記錄。
變數 | 描述 |
---|---|
FIELDWIDTHS | 由空格分隔的一列數字,定義每個資料欄位的寬度 |
FS | 輸入欄位分隔符(預設是空格和tab) |
RS | 輸入記錄分隔符(預設是換行) |
OFS | 輸出欄位分隔符 |
ORS | 輸出記錄分隔符 |
~
FIELDWIDTHS允許你不依靠欄位分隔符來讀取記錄,一旦設定了FIELDWIDTHS,就會忽略FS變數,並且不能再次改變,不適合用於變長的欄位。
#data1
AAAAAHjgisg jkgkj
232 jlj
hoihgl
5555-2424
BBBBLhojlgs jk
435 jk
onnaga
5252-2525
CCCCQjlsjt jlkg
435 jlkjl
opjgsn
4521-5929
如上所示,我們如果要把每四行當做一個記錄讀取呢?很顯然FS應該設定為換行符\n,那麼RS應該怎麼設定呢?我們可以用空白行來當做記錄分隔符。可以寫出以下awk程式碼:
BEGIN {
FS="\n";RS="";OFS="-"
}
{print $1,$4}
END{
print "This is the end line."
}
執行之後得到輸出,我們使用了輸出欄位分隔符-來分隔輸出,如下圖:
2、資料變數
除了FS和RS等一系列變數,還有一些內建變數來幫助你瞭解資料發上了什麼變化。主要的變數如下表:
變數 | 描述 |
---|---|
ARGC | 當前命令列引數個數,ARGument COUNT |
ARGV | 包含命令列引數的陣列 |
ARGIND | 當前檔案在ARGV中的位置 |
ENVIRON | 當前shell環境變數及其值組成的關聯陣列 |
FILENAME | 用作awk輸入資料的資料檔案的檔名 |
FNR | 當前資料檔案中的資料行數 |
NF | 資料檔案中欄位總數 |
NR | 已處理的輸入記錄數 |
… | … |
~
見如下使用NF和NR的示例,當我們不知道一條記錄裡面的欄位時,可以用$NF來引用最後一個欄位;沒處理一條記錄時NR的值就會加1,注意在指令碼中引用awk變數時,變數名前不用加美元符號,這和shell中引用變數不同,如下面的NR。
#script1.awk
BEGIN {
FS=":";OFS="-"
}
{print $1,$NF,NR}
END{
}
#awk指令碼
awk -f script1.awk /etc/passwd
執行後,可得到以下期望的輸出:
2.2、自定義變數:
1、在指令碼中給變數賦值
和shell指令碼中一致,有賦值語句即可。並且還支援數值運算,如下:
2、在命令列中給變數賦值
如下所示,我們在BEGIN和命令當中用到了變數n,但是還沒有定義,可以在命令列中用n=
的方式來為n指定數值。
BEGIN {
print "Print the ",n," field."
}
{print $n}
END{
}
需要注意的是在命令列中設定了變數值以後,BEGIN中並不可以直接用!這時我們要在變數定義的前面加上-v 來在BEGIN程式碼之前設定變數。
三、awk使用陣列
awk中使用的是關聯陣列,它和數字陣列不同的是索引可以是任意文字字串。其實,關聯陣列和字典、散列表這些東西是一個概念的。
1、定義陣列變數
var[key] = value
2、遍歷陣列變數
和多數語言一樣,遍歷陣列採取for循換即可:
for(var in array)
{
statements
}
輸出如下所示,可以看出對陣列的支援還是不錯的,足夠我們使用。
[email protected]:~$ awk -f script1.awk
key: Onlsgn value: Lkongn
key: Ajgsl value: Ihono
key: Qlgns value: Pnwl
key: 1 value: 1
key: 2 value: 2
key: 3 value: 3
4
1
3、刪除陣列變數
刪除陣列索引就可以了:delete array[key]
四、使用模式匹配
awk中同樣支援使用模式匹配來過濾資料記錄,這和sed中的很相似。
1、正則表示式(re)
使用re時,必須使它出現在它要控制的程式指令碼的左花括號前。使用起來很簡單,如下圖所示:
2、匹配操作符
使用匹配操作符來更精確的控制將要匹配的欄位,$n ~ /expression/
,即這裡匹配了滿足第n個欄位是expression的情況。請看以下簡單示例:
#匹配/etc/passwd檔案中第一個欄位一sys開頭的記錄,並輸出第一個欄位
lupu@ubuntu:~$ awk -F: '$1 ~ /^sys/{print $1}' /etc/passwd
sys
systemd-timesync
systemd-network
systemd-resolve
systemd-bus-proxy
syslog
我們也可以用!來排除正則表示式的匹配,$n ! ~ /expression/
。
3、數學表示式
當匹配資料欄位中有數值時,用數學表示式來匹配非常方便。比如我們用命令ps -ef > ps.log
將當前程序資訊寫入檔案中,如果我們需要找出UID大於9000的程序那麼可以這麼來寫命令:awk '$2 > 9000{print $0}' ps.log
。
可以使用任何比較常見的表示式。
- x == y
- x <= y
- x >= y
- x < y
- x > y
也可以對文字資料使用表示式,但是必須注意的是,只有表示式完全一樣才可以匹配。不能使用正則表示式。