回到: "Linux系列文章" "Shell系列文章" "Awk系列文章" 數組 awk數組特性: awk的數組是關聯數組(即key/value方式的hash數據結構),索引下標可為數值(甚至是負數、小數等),也可為字元串 在內部,awk數組的索引全都是字元串,即使是數值索引在使用時內部也會轉換成字 ...
回到:
數組
awk數組特性:
- awk的數組是關聯數組(即key/value方式的hash數據結構),索引下標可為數值(甚至是負數、小數等),也可為字元串
- 在內部,awk數組的索引全都是字元串,即使是數值索引在使用時內部也會轉換成字元串
- awk的數組元素的順序和元素插入時的順序很可能是不相同的
- 在內部,awk數組的索引全都是字元串,即使是數值索引在使用時內部也會轉換成字元串
- awk數組支持數組的數組
awk訪問、賦值數組元素
arr[idx]
arr[idx] = value
索引可以是整數、負數、0、小數、字元串。如果是數值索引,會按照CONVFMT變數指定的格式先轉換成字元串。
例如:
awk '
BEGIN{
arr[1] = 11
arr["1"] = 111
arr["a"] = "aa"
arr[-1] = -11
arr[4.3] = 4.33
# 本文來自駿馬金龍:www.junmajinlong.com
print arr[1] # 111
print arr["1"] # 111
print arr["a"] # aa
print arr[-1] # -11
print arr[4.3] # 4.33
}
'
通過索引的方式訪問數組中不存在的元素時,會返回空字元串,同時會創建這個元素並將其值設置為空字元串。
awk '
BEGIN{
arr[-1]=3;
print length(arr); # 1
print arr[1];
print length(arr) # 2
}'
awk數組長度
awk提供了length()
函數來獲取數組的元素個數,它也可以用於獲取字元串的字元數量。還可以獲取數值轉換成字元串後的字元數量。
awk 'BEGIN{arr[1]=1;arr[2]=2;print length(arr);print length("hello")}'
awk刪除數組元素
delete arr[idx]
:刪除數組arr[idx]
元素- 刪除不存在的元素不會報錯
- 刪除不存在的元素不會報錯
delete arr
:刪除數組所有元素
$ awk 'BEGIN{arr[1]=1;arr[2]=2;arr[3]=3;delete arr[2];print length(arr)}'
2
awk檢測是否是數組
isarray(arr)
可用於檢測arr是否是數組,如果是數組則返回1,否則返回0。
typeof(arr)
可返回數據類型,如果arr是數組,則其返回"array"。
awk 'BEGIN{
arr[1]=1;
print isarray(arr);
print (typeof(arr) == "array")
}'
awk測試元素是否在數組中
不要使用下麵的方式來測試元素是否在數組中:
if(arr["x"] != ""){...}
這有兩個問題:
- 如果不存在arr["x"],則會立即創建該元素,並將其值設置為空字元串
- 有些元素的值本身就是空字元串
應當使用數組成員測試操作符in來測試:
# 註意,idx不要使用index,它是一個內置函數
if (idx in arr){...}
它會測試索引idx是否在數組中,如果存在則返回1,不存在則返回0。
awk '
BEGIN{
# 本文來自駿馬金龍:www.junmajinlong.com
arr[1]=1;
arr[2]=2;
arr[3]=3;
arr[1]="";
delete arr[2];
print (1 in arr); # 1
print (2 in arr); # 0
}'
awk遍曆數組
awk提供了一種for變體來遍曆數組:
for(idx in arr){print arr[idx]}
因為awk數組是關聯數組,元素是不連續的,也就是說沒有順序。遍歷awk數組時,順序是不可預測的。
例如:
# 本文來自駿馬金龍:www.junmajinlong.com
awk '
BEGIN{
arr["one"] = 1
arr["two"] = 2
arr["three"] = 3
arr["four"] = 4
arr["five"] = 5
for(i in arr){
print i " -> " arr[i]
}
}
'
此外,不要隨意使用for(i=0;i<length(arr);i++)
來遍曆數組,因為awk數組是關聯數組。但如果已經明確知道數組的所有元素索引都位於某個數值範圍內,則可以使用該方式進行遍歷。
例如:
# 本文來自駿馬金龍:www.junmajinlong.com
awk '
BEGIN{
arr[1] = "one"
arr[2] = "two"
arr[3] = "three"
arr[4] = "four"
arr[5] = "five"
arr[10]= "ten"
for(i=0;i<=10;i++){
if(i in arr){
print arr[i]
}
}
}
'
awk複雜索引的數組
在awk中,很多時候單純的一個數組只能存放兩個信息:一個索引、一個值。但在一些場景下,這樣簡單的存儲能力在處理複雜需求的時候可能會捉襟見肘。
為了存儲更多信息,方式之一是將第3份、第4份等信息全部以特殊方式存放到值中,但是這樣的方式在實際使用過程中並不方便,每次都需要去分割值從而取出各部分的值。
另一種方式是將第3份、第4份等信息存放在索引中,將多份數據組成一個整體構成一個索引。
gawk中提供了將多份數據信息組合成一個整體當作一個索引的功能。預設方式為arr[x,y]
,其中x和y是要結合起來構建成一個索引的兩部分數據信息。逗號稱為下標分隔符,在構建索引時會根據預定義變數SUBSEP的值將多個索引組合起來。所以arr[x,y]
其實完全等價於arr[x SUBSEP y]
。
例如,如果SUBSEP設置為"@",那麼arr[5,12] = 512
存儲時,其真實索引為5@12
,所以要訪問該元素需使用arr["5@12"]
。
SUBSEP的預設值為\034
,它是一個不可列印的字元,幾乎不可能會出現在字元串當中。
如果我們願意的話,我們也可以自己將多份數據組合起來去構建成一個索引,例如arr[x" "y]
。但是awk提供了這種更為簡便的方式,直接用即可。
為了測試這種複雜數組的索引是否在數組中,可以使用如下方式:
arr["a","b"] = 12
if (("a", "b") in arr){...}
例如,順時針倒轉下列數據:
1 2 3 4 5 6
2 3 4 5 6 1
3 4 5 6 1 2
4 5 6 1 2 3
結果:
4 3 2 1
5 4 3 2
6 5 4 3
1 6 5 4
2 1 6 5
3 2 1 6
{
nf = NF
nr = NR
for(i=1;i<=NF;i++){
arr[NR,i] = $i
}
}
END{
for(i=1;i<=nf;i++){
for(j=nr;j>=1;j--){
if(j%nr == 1){
printf "%s\n", arr[j,i]
}else {
printf "%s ", arr[j,i]
}
}
}
}
awk子數組
子數組是指數組中的元素也是一個數組,即Array of Array,它也稱為子數組(subarray)。
awk也支持子數組,在效果上即是嵌套數組或多維數組。
a[1][1] = 11
a[1][2] = 12
a[1][3] = 13
a[2][1] = 21
a[2][2] = 22
a[2][3] = 23
a[2][4][1] = 241
a[2][4][2] = 242
a[2][4][1] = 241
a[2][4][3] = 243
通過如下方式遍歷二維數組:
for(i in a){
for (j in a[i]){
if(isarray(a[i][j])){
continue
}
print a[i][j]
}
}
awk指定數組遍歷順序
由於awk數組是關聯數組,預設情況下,for(idx in arr)
遍曆數組時順序是不可預測的。
但是gawk提供了PROCINFO["sorted_in"]
來指定遍歷的元素順序。它可以設置為兩種類型的值:
- 設置為用戶自定義函數
- 設置為下麵這些awk預定義好的值:
@unsorted
:預設值,遍歷時無序
@ind_str_asc
:索引按字元串比較方式升序遍歷
@ind_str_desc
:索引按字元串比較方式降序遍歷
@ind_num_asc
:索引強制按照數值比較方式升序遍歷。所以無法轉換為數值的字元串索引將當作數值0進行比較
@ind_num_desc
:索引強制按照數值比較方式降序遍歷。所以無法轉換為數值的字元串索引將當作數值0進行比較
@val_type_asc
:按值升序比較,此外數值類型出現在前面,接著是字元串類型,最後是數組類(即認為num<str<arr
)
@val_type_desc
:按值降序比較,此外數組類型出現在前面,接著是字元串類型,最後是數值型(即認為num<str<arr
)
@val_str_asc
:按值升序比較,數值轉換成字元串再比較,而數組出現在尾部(即認str<arr
)
@val_str_desc
:按值降序比較,數值轉換成字元串再比較,而數組出現在頭部(即認str<arr
)
@val_num_asc
:按值升序比較,字元串轉換成數值再比較,而數組出現在尾部(即認num<arr
)
@val_num_desc
:按值降序比較,字元串轉換成數值再比較,而數組出現在頭部(即認為num<arr
)
例如:
awk '
BEGIN{
arr[1] = "one"
arr[2] = "two"
arr[3] = "three"
arr["a"] ="aa"
arr["b"] ="bb"
arr[10]= "ten"
#PROCINFO["sorted_in"] = "@ind_num_asc"
#PROCINFO["sorted_in"] = "@ind_str_asc"
PROCINFO["sorted_in"] = "@val_str_asc"
for(idx in arr){
print idx " -> " arr[idx]
}
}'
a -> aa
b -> bb
1 -> one
2 -> two
3 -> three
10 -> ten
# 本文來自駿馬金龍:www.junmajinlong.com
如果指定為用戶自定義的排序函數,其函數格式為:
function sort_func(i1,v1,i2,v2){
...
return <0;0;>0
}
其中,i1和i2是每次所取兩個元素的索引,v1和v2是這兩個索引的對應值。
如果返回值小於0,則表示i1在i2前面,i1先被遍歷。如果等於0,則表示i1和i2具有等值關係,它們的遍歷順序不可保證。如果大於0,則表示i2先於i1被遍歷。
例如,對數組元素按數值大小比較來決定遍歷順序。
awk '
function cmp_val_num(i1, v1, i2, v2){
if ((v1 - v2) < 0) {
return -1
} else if ((v1 - v2) == 0) {
return 0
} else {
return 1
}
# return (v1-v2)
}
NR > 1 {
arr[$0] = $4
}
END {
PROCINFO["sorted_in"] = "cmp_val_num"
for (i in arr) {
print i
}
}' a.txt
再比如,按數組元素值的字元大小來比較。
function cmp_val_str(i1,v1,i2,v2) {
v1 = v1 ""
v2 = v2 ""
if(v1 < v2){
return -1
} else if(v1 == v2){
return 0
} else {
return 1
}
# return (v1 < v2) ? -1 : (v1 != v2)
}
NR>1{
arr[$0] = $2
}
END{
PROCINFO["sorted_in"] = "cmp_val_str"
for(line in arr)
{
print line
}
}
再比如,對元素值按數值升序比較,且相等時再按第一個欄位ID進行數值降序比較。
awk '
function cmp_val_num(i1,v1,i2,v2, a1,a2) {
if (v1<v2) {
return - 1
} else if(v1 == v2){
split(i1, a1, SUBSEP)
split(i2, a2, SUBSEP)
return a2[2] - a1[2]
} else {
return 1
}
}
NR>1{
arr[$0,$1] = $4
}
END{
PROCINFO["sorted_in"] = "cmp_val_num"
for(str in arr){
split(str, a, SUBSEP)
print a[1]
}
}
' a.txt
上面使用的arr[x,y]
來存儲額外信息,下麵使用arr[x][y]
多維數組的方式來存儲額外信息實現同樣的排序功能。
NR>1{
arr[NR][$0] = $4
}
END{
PROCINFO["sorted_in"] = "cmp_val_num"
for(nr in arr){
for(line in arr[nr]){
print line
}
# 本文來自駿馬金龍:www.junmajinlong.com
}
}
function cmp_val_num(i1,v1,i2,v2, ii1,ii2){
# 獲取v1/v2的索引,即$0的值
for(ii1 in v1){ }
for(ii2 in v2){ }
if(v1[ii1] < v2[ii2]){
return -1
}else if(v1[ii1] > v2[ii2]){
return 1
}else{
return (i2 - i1)
}
}
此外,gawk還提供了兩個內置函數asort()和asorti()來對數組進行排序。