awk 命令
awk 簡述
awk [選項] '程式' file ...
|
設定欄位分隔字元,預設為「空白字元」或製表字元 (Tab 鍵)。 |
|
將變數 var 設定成 value。 |
- 記錄和欄位
-
AWK 可以處理文字檔案和串流 (管道),輸入資料分為「記錄」和「欄位」。awk 一次對一條「記錄」進行操作,直到結尾 (EOF)。
「記錄」由「記錄分隔字元RS
(Record Separator)」來分隔,預設的RS
是換行字元,表示文字資料中的每一行都是一條記錄,可以使用變數設定不同的RS
。
一行記錄可由「欄位分隔字元FS
(Field Separator)」分隔出不同欄位,預設的FS
是空格或製表字元 (Tab 鍵),可採用命令列選項-F
或變數設定不同的FS
。每個欄位可由欄位變數 ($
) 取得 $1 $2 $3 依此類推,$0 為全部內容,最後一個欄位也可以使用變數$NF
。FS
(欄位分隔字元) 設定為:
,將/etc/passwd
檔案標示出欄位如下:$grep ubuntu /etc/passwd ubuntu:x:1000:1000::/home/ubuntu:/bin/bash $1 :$2 :$4 :$6 :$7($NF) :$3 :$5 $0----------------------------------------
awk 程式 (program)
- 「程式 (program)」是由一系列模式動作對組寫成:
-
'pattern {action}'
其中
pattern
(模式) 表示在資料中找出符合的「記錄(行)」,可為邏輯判斷式或正規表示式 (Regex),當省略pattern
時,表示匹配所有記錄(行)。
而{action}
是在匹配記錄時所執行的動作,由大括號{}
中的語句 (statement) 組成,{action}
也可省略,省略時預設動作是列印該行的全部內容 (print $0
)。
語句 (statement) 可以解讀為「指令」。一個動作 (action) 有多個指令 (語句) 時,要寫在下一行或以分號 (;
) 分開,如下:awk -F: ' { print $1 print $6 }' /etc/passwd
awk -F: '{print $1; print $6}' /etc/passwd
另外在「控制語句」 (如if
) 中,如果有多個指令也需要由大括號{}
組成複合語句,如下:awk -F: ' { if ($3 >= 1000) { print $1 print $3 } }' /etc/passwd
awk 模式 (pattern)
- 模式 (pattern) 判斷式
-
邏輯判斷語法
x == y
相等
x != y
不相等
x > y
大於
x >= y
大於或等於
x < y
小於
x <= y
小於或等於
正規表示式語法
/Regex/
匹配 Regex 表示式,表示式必須以定界字元
/
包圍。!/Regex/
否定匹配 Regex 表示式。
x ~ y
x 匹配 y 正規表示式。
x !~ y
x 不匹配 y 正規表示式。
AND OR 語法,適用邏輯判斷式及正規表示式。
&&
AND 條件
||
OR 條件
以邏輯判斷式,列出UID
大於或等於 1000 的帳戶awk -F: '($3 >= 1000) {print $1, $6}' /etc/passwd
列出用戶名及家目錄為第 1 個欄位及第 6 個欄位
{print $1, $6}
,欄位變數之間的逗號 (,
) 是列印欄位分隔字元OFS
(Output field separator) 預設為空白字元。如果是{print $1 $6}
並不會把欄位分隔,可採用" "
字串,在字串中加入空格,如{print $1 " " $6}
。以控制語句,列出UID
大於或等於 1000 的帳戶awk -F: '{if ($3 >= 1000) print $1 " " $6}' /etc/passwd
以正規表示式 (Regex) 匹配模式 (pattern),過濾具有家目錄的帳戶awk -F: '/\/home/ {print $1, $6}' /etc/passwd
將記錄中整行的內容匹配模式
/\/home/
,正規表示式必須以定界字元/
包圍,而字元/
必須以跳脫字元\
轉譯成\/
,內容匹配/home
才會列印。匹配 (~
) 兩個運算,$6 ~ /\/home/
為欄位 6 匹配/home
,本例的結果跟上面是一樣,只是上面是不區分欄位。awk -F: '$6 ~ /\/home/ {print $1, $6}' /etc/passwd
- 以模式指定記錄範圍
-
pattern1, pattern2
採用逗號 (
,
) 區分兩個位置;位置範圍由第一個位置pattern1
至 (,
) 第二個位置pattern2
, 第二個開始匹配是在第一個位置或之後,記錄範圍始終跨越至少一個記錄 (行)。列印記錄內容 2 至 3 的記錄seq 5 | awk '$0==2, $0==3' seq 5 | awk '/2/, /3/'
可混合「邏輯判斷式」及「正規表示式」seq 5 | awk '$0==2, /3/'
列印記錄內容 3 至 2,由於第二個位置不合乎條件,將列印第一個位置記錄 (3
) 至最後記錄 (5
)seq 5 | awk '/3/, /2/'
列印記錄內容 2 或 3 或 Yes 的記錄echo Yes | awk '/2/ || /3/ || /Yes/'
在範圍模式中,逗號 (,
) 是所有運算子中最低的優先權 (即;最後判斷)。因此,範圍模式與另一個運算結合起來:echo Yes | awk '/2/,/3/ || /Yes/'
程式的意圖是(/1/,/2/) || /Yes/
,但 awk 將其解讀為/1/, (/2/ || /Yes/)
,這無法改變或解決;範圍模式不能與其他模式結合:$echo Yes | awk '(/1/,/2/) || /Yes/' awk: cmd. line:1: (/1/,/2/) || /Yes/ awk: cmd. line:1: ^ syntax error
採用「控制語句」來處理:echo Yes | awk '{if ( ($0>=2 && $0<=3) || ($0=="Yes") ) print}'
- 多條模式 (pattern)
-
在程式中可以有多條模式,合乎模式的以不同動作來處理。
awk -F: ' $1=="root" { print "The superuser is on line ", NR } $1=="www-data" { print "The user for the web server is on line ", NR } ' /etc/passwd
- BEGIN 和 END 特殊模式 (pattern)
-
BEGIN { 尚未讀取前先執行的動作 } BEGINFILE { 檔案尚未讀取的動作 } { 讀取時執行的動作 } ENDFILE { 檔案讀取後執行的動作 } END { 讀取完成後執行的動作 }
當程式 (program) 只有
BEGIN
時,並不需要讀取檔案或管道輸入。
在執行BEGIN
時並未開啟及讀取檔案,BEGINFILE
為開啟檔案,尚未讀取,再執行讀取時執行的動作
,檔案讀取完成後執行ENDFILE
,所有檔案處理完畢後最後執行END
。當傳入多個檔案時,處理檔案按順序 (先開啟左邊的檔案) 把多個檔案當成接續的 (一個) 檔案,其行號
NR
為接續。awk ' BEGIN { print "BEGIN FILENAME is", FILENAME } BEGINFILE { print "BEGINFILE", FILENAME, NR } { if (LastFileName != FILENAME) { LastFileName = FILENAME print "Read file", FILENAME, NR } } ENDFILE { print "ENDFILE", FILENAME, NR } END { print "END FILENAME is", FILENAME } ' /etc/passwd /etc/group
BEGIN FILENAME is (1) BEGINFILE /etc/passwd 0 (2) Read file /etc/passwd 1 (3) ENDFILE /etc/passwd 27 (4) BEGINFILE /etc/group 27 Read file /etc/group 28 ENDFILE /etc/group 81 END FILENAME is /etc/group (5)
1 BEGIN
未開啟及讀取檔案2 BEGINFILE
開啟檔案,尚未讀取檔案。3 執行「讀取時的動作」讀取檔案。 4 ENDFILE
檔案讀取完成。5 END
所有檔案處理完畢。
awk 變數
- 內建變數
-
ARGC
命令列輸入參數數量
ARGV
命令列輸入參數內容 (陣列)
NR
(目前) 記錄位置 (行數)。
NF
(目前) 記錄中欄位數。
FILENAME
檔案名稱。
FS
(輸入的) 欄位分隔字元 (Field Separator),預設為「空白字元」或製表字元 (Tab 鍵)。
RS
(輸入的) 記錄分隔字元 (Record Separator),預設為換行字元。
OFS
輸出欄位分隔字元 (Output Field Separator),預設值為空白字元。
ORS
輸出記錄分隔字元 (Output Record Separator),預設值為換行字元。
OFMT
輸出數字格式 (Format for numeric output),預設值為
%.6g
。IGNORECASE
匹配或排序時不區分大小寫,預設值為 1。
RSTART
字串函數 match 專用,回傳匹配的 (開始) 位置。
RLENGTH
字串函數 match 專用,回傳匹配的 (內容) 長度。
SUBSEP
(多主鍵) 陣列分隔字元。
FPAT
匹配
FPAT
正規表示法,以匹配內容建立欄位,預設為[^[:space:]]+
(不為空格的多個字元)。
在程式中同時設定變數FS
、FPAT
,是以最後設定哪個變數來解析欄位,未設定變數預設是以FS
來分隔。CSV
是以逗號,
來區分欄位,但字串中也會有逗號的情況如1,"2,b",3
,這時只能用FPAT = "[^,]+|\"[^\"]+\""
來處理。上述的正規表示法的意思是-
匹配不為逗號
[^,]
多個 (+
) 字元 -
或
|
-
前後為雙引號
\"
中間不為雙引號[^\"]
多個 (+
) 字元
列印具有家目錄的帳戶及行數$awk -F: '/\/home/ {print $1,NR}' /etc/passwd syslog 21 ubuntu 24
以BEGIN
在讀取前先設定FS
(輸入) 欄位分隔字元為:
,OFS
輸出欄位分隔字元為,
:awk 'BEGIN {FS=":"; OFS=","} /\/home/ {print $1,NR}' /etc/passwd
以命令列選項-v
設定內建變數awk -vFS=: -vOFS=, '/\/home/ {print $1,NR}' /etc/passwd
設定OFMT
輸出數字格式示例awk 'BEGIN{OFMT="%.0f"; print 2.5, 3.5, 4.5, 5.5}'
2 4 4 6
awk 的浮點進位為銀行進位法 (Banker’s Rounding) 可簡記為「四捨六入五成雙」,五成雙,尾數「5」的前一位,若雙數則為雙數,若為單數則進位。
如格式化字串為%.0f
,參數輸入2.5, 3.5, 4.5, 5.5
則輸出2, 4, 4, 6
。 -
- 自定變數
-
awk 自定變數可為任意型別,如數字、浮點、字串或陣列。變數名稱不可跟函數名稱相同,如
index = 1
(index 剛好為函數),由於變數區分大小寫,可以改名為Index = 1
。變數內容指定為 16 進制數值,則內容值前置
0x
,如myVar = 0x10
(10 進制為 16),x
或數值a
至f
不區分為大小寫可混用。
8 進制則為前置0
如myVar = 010
(10 進制為 8)。
num=123
awk -v n=$num 'BEGIN {print n}'
awk 控制語句
{action}
控制語句語法if (conditional)
then-body-statement
else if (conditional)
else-if-then-body-statement
else
else-body-statement
initialization-statement
while (conditional) {
body-statement
increment-statement
break
continue
}
for (initialization; condition; increment)
body-statement
break
continue
for (variable in array)
body-statement
break
continue
switch (expression) {
case value or regular_expression:
case-body-statement
break
default:
default-body-statement
break
}
next
exit [return_code]
nextfile
- next
-
處理下一筆記錄,若不為
EOF
重啟循環,回到「讀取時執行的動作」程式開始位置 (不會執行next
後面的程式)。
awk 函數
print
內的逗號 ,
是列印 OFS
欄位分隔字元,數字輸出格式為 OFMT
(預設為 %.6g
),執行 print
後會加印 ORS
輸出記錄分隔字元 (預設為換行字元)。可設定內建變數 OFS
、OFMT
及 ORS
改變預設行為。print $1 $2
並不會把欄位分隔,可採用 " "
字串,在字串中加入空格,如 print $1 " " $2
。
print
無參數時為 print $0
,另外在 awk 程式 (program) 的 pattern {action}
,當省略 {action}
時預設動作也是 print $0
。
printf
printf format [, argument-list]
format (格式) 為字串,argument-list 是對應於 format 的參數列表。
% [parameter] [flags] [width] [.precision] type
parameter (參數) |
|
N$ |
列印第 N 個參數,如 |
flags (修飾字元) |
|
|
左對齊,不足寬度時在右邊填滿空格,如 |
|
用於數字;在正數前面增加一個 |
space |
用於數字;在正數前面增加一個空格 (space),主要是用於跟負數對齊。如 |
|
用於數字;不足寬度時在左邊填滿 |
|
用於數字;加上千位分隔符號;支援千位分隔表示的 由於
' (單引號) 需要轉譯,以 16 進制 \x27 避開。以下列示處理 ' (單引號) 的方式:
|
|
類型 (type) 為 |
width (寬度) |
|
「寬度」指定輸出的最少字元數,通常用於固定寬度的輸出。 「寬度」可以省略,或者當作為另一個參數傳遞時由星號 |
|
precision (精度) |
|
「精度」通常指定輸出的最多字元數,在不同 type (類型) 其義意不同:
「精度」可以省略,或者當作為另一個參數傳遞時由星號 |
|
type (類型) |
|
|
字元,參數為數字如 |
|
整數,無條件捨去法。 |
|
以科學記號顯示浮點數,小寫 |
|
浮點數值格式化成定點 (fixed-point)。 |
|
通用浮點型,自動顯示成「浮點 (定點) 類型」或「科學記號類型」,整數位數已經超過由「精度」指定的有效位數,則會顯示成科學記號。小寫 |
|
8 進制。 |
|
字串。 |
|
16 進制, |
|
輸出一個 |
|
Alert (警報) 嗶聲 (beep), Ctrl+g,ASCII code 7 (BEL). |
|
倒退键 Ctrl+h,ASCII code 8 (BS). |
|
製表字元,Tab、Ctrl+i,ASCII code 9 (HT). |
|
換行字元,Ctrl+j,ASCII code 10 (LF). |
|
垂直製表字元,Ctrl+k,ASCII code 11 (VT). |
|
換頁字元,Ctrl+l,ASCII code 12 (FF). |
|
確認鍵,Ctrl+m,ASCII code 13 (CR). |
|
8 進制值 nnn,其中 nnn 為 |
|
16 進制數值 hh( |
|
輸出一個 |
|
在字串中輸出 |
數學函數
|
x 除以 y 的餘數。 |
|
x 的 y 次方 |
|
正弦 |
|
餘弦 |
|
反正切 |
|
指數 |
|
對數 |
|
開根號 |
|
整數值 (無條件捨去小數) |
|
初始化亂數 |
|
回傳亂數 n,0 <= n < 1。 |
位元操作函數
|
|
回傳位元運算 |
|
|
|
回傳 val 位元運算的補數 (complement),將 |
|
|
|
回傳 val 以位元 (bit) 左移 count 位,(左邊) 高位拋棄,(右邊) 低位補 0。 |
|
|
|
回傳位元運算 |
|
|
|
回傳 val 以位元 (bit) 右移 count 位,(左邊) 高位補 0,(右邊) 低位拋棄。 |
|
|
|
回傳位元運算 |
字串函數
|
|
將 source 的「內容值」排序。排序後的陣列主鍵由整數 1 開始,陣列內容為「內容值」。如果有指定 dest 則排序結果會儲存在 dest (source 不變)。內建變數 |
|
|
|
將 source 的「主鍵值」排序,排序後的陣列主鍵由整數 1 開始,陣列內容為「主鍵值」。如果有指定 dest 則排序結果會儲存在 dest (source 不變)。內建變數 |
|
|
|
將輸入的 source 匹配 regexp 替換為 replacement 回傳替換後的文字。
未指定 source 參數其來源為欄位 $0,how 設定為數字 n 表示第 n 次開始替換,若為 |
|
|
|
回傳字串 find 在 in 的位置 (不支援正規表示法)。 |
|
|
|
回傳字串長度,如果沒有 string 參數則回傳 |
|
|
|
取得 string 匹配 regexp 的位置和長度,將開始位置填入內建變數 array 為正規表示法中分組功能的回撥
|
|
|
|
字串 string 匹配 fieldpat 模式,將合乎模式匹配的文字建立陣列 array,如省略 fieldpat 預設為內建變數 |
|
|
|
字串 string 分割後 (的前後) 建立陣列 array,如省略 fieldsep 預設為內建變數 |
|
|
|
回傳(不列印)printf (使用相同參數) 輸出的字串。 |
|
|
|
將 str 轉成數字 (含小數),若 str 前置 |
|
|
|
將 target 匹配 regexp 替換一次成為 replacement,替換後的文字取代 target。沒有指定 target 參數時預設為欄位變數 |
|
|
|
gsub 跟 sub 類似,sub 只替換一次,gsub 為全部替換。 |
|
|
|
回傳字串 string 開始位置 start 長度 length,沒有指定 length 則至 string 的結尾。 |
|
|
|
轉成小寫 |
|
|
|
轉成大寫 |
輸入輸出函數
|
|
輸入的檔案或管道,在經過讀取後,其讀取的位置會增加,如果不關閉輸入,則後續的讀取是接續上次的位置。 在雙向管道 (two-way pipe) 或稱為 coprocess (協助) 可以指定 how 參數, 若 |
|
|
|
將輸出檔案或管道的緩衝區排清 (flush) 確定已寫入。 |
|
|
|
執行操作系統的命令 command 回傳命令的狀態碼。 |
|
|
|
讀取下一筆記錄儲存至 target 並回傳讀取狀態,沒有指定 target 時將儲存至 執行
getline 錯誤時並不會出錯,而是回傳狀態:
採用
|
getent hosts 8.8.8.8
8.8.8.8 dns.google
getline
取得 IP 位置的名稱echo -e '8.8.8.8\n8.8.4.4' | awk '
{
"getent hosts " $0 | getline Hosts;
split(Hosts, Hosts_Arr, " ");
print $0, Hosts_Arr[2]
}'
8.8.8.8 dns.google 8.8.4.4 dns.google
awk -F: '
{
User = $1
UID = $3
while ( (getline < "/etc/group") > 0 )
{
if (User == $1) {
print $1, UID, $3
break
}
}
close("/etc/group") (1)
}' /etc/passwd
1 | 關閉檔案後,第二次才能從頭開始讀取。 |
註:本例是為了說明 close
,並未最佳化,在 BEGIN{}
時先將 /etc/group
全部讀取至陣列再以陣列查表,比較理想。
- print printf 重定向輸出
-
print
及printf
的管道輸出字元其運作跟管道一樣。print > "filename" print >> "filename" print | "command" print |& "command_coprocess"
- 將輸出檔案錯誤視為非嚴重錯誤
-
當
PROCINFO["NONFATAL"]
或者PROCINFO["filename", "NONFATAL"]
存在時 (不管項目設定值,設為 0 也一樣),則print
重定向至檔案的 I/O 錯誤將變為非嚴重錯誤 (註:awk 5.0 才會作用)。awk ' BEGIN { PROCINFO["/no/such/file", "NONFATAL"] = 0 (1) ERRNO = 0 print "hi" > "/no/such/file" if (ERRNO) { print("Output failed:", ERRNO) > "/dev/stderr" exit 1 } }'
1 設定為 0 是在解釋該項目存在時即有作用,實際上不需要設定;或者設定為 1。 Output failed: No such file or directory
- 雙向管道
-
將管道的輸入及輸出;由
print
傳送給命令的「管道輸入」;由getline
讀取命令的「管道輸出」。以|&
建立雙向管道print |& command command |& getline x
|
跟|&
可能混淆以為行為一樣,| command
是由命令直接輸出,而|& command
其命令是回傳給 awk,命令並不會輸出。依據命令的特性,如命令是以逐行來處理,則每傳送一筆記錄後執行
close(command, "to")
在管道中寫入EOF
,避免要求讀取所有資料後才會處理的「命令」發生阻塞 (永久等待)。
傳送後執行關閉管道的輸出close(command, "to")
,在執行getline
之後也需要執行close(command)
,這兩個close
是成對的。{ print |& command close(command, "to") (1) if ( (command |& getline x) > 0 ) print x close(command) (2) }
1 close(command, "to")
在管道中寫入EOF
。2 在處理後需要執行 close(command)
,這兩個close
是成對的。如果命令是讀取所有的輸入後才處理 (或者是讀取全部後才會正確),那麼則分段;先傳送所有記錄後,再傳送
EOF
,再以getline
(全部) 讀取。{ print |& command (1) } END { close(command, "to") (2) while ( (command |& getline x) > 0) { print x } close(command) (3) }
1 先傳送所有記錄。 2 傳送所有記錄後,再傳送 EOF
。3 實際上並不需要該指令,但加入該指定比較合乎 close
的用法。將使用者帳戶轉成大寫 (逐行處理)awk -F: ' BEGIN { command="awk \x27{ print toupper($0) }\x27" (1) } { print $1 |& command close(command, "to") if ( (command |& getline x) > 0 ) print x close(command) } ' /etc/passwd
1 command
即為awk '{ print toupper($0) }'
,將 ' (單引號) 以 16 進制\x27
避開轉譯。將使用者帳戶經過cat -n
加上行號,讀取後再列印 (全部資料分段處理)。awk -F: ' BEGIN { command="cat -n" } { print $1 |& command } END { close(command, "to") while ( (command |& getline x) > 0 ) print x close(command) } ' /etc/passwd
awk '
BEGIN {
command = "echo hi"
print "getline return:" (command | getline)
print "close return:" close(command)
print "----------"
command = "sleep 1 && echo hi"
PROCINFO[command, "RETRY"] = 1
PROCINFO[command, "READ_TIMEOUT"] = 200
print "getline return:" (command | getline), "ERRNO:" ERRNO
print "close return:" close(command), "ERRNO:" ERRNO
print "close return:" close(command), "ERRNO:" ERRNO
}'
getline return:1 close return:0 ---------- getline return:-2 ERRNO:Connection timed out (1) close return:269 ERRNO: (2) close return:-1 ERRNO:close of redirection that was never opened
1 | 不一定是 -2,awk 4.1.4 為 -1。 |
2 | 不一定是 269 可能是其他非 0 的數字。 |
awk 關聯陣列
陣列主鍵值為唯一,陣列主鍵 (及內容值) 可為文字或數字,收集或統計欄位的情況。
- 陣列類型
-
-
array[x,y]
多主鍵陣列
水果有多種顏色,水果及顏色為同一組資料時,可採用多主鍵陣列。 -
array[x][y]
多維陣列
水果有多種顏色,如要各別列出水果、顏色,處理不同的維度時,應採用多維陣列。
多主鍵陣列是儲存在一維陣列,其主鍵是以SUBSEP
(陣列分隔字元) 來分隔,該字元即為 Ctrl+\。awk ' BEGIN { a[1,1] = 11; a[1,2] = 12; a[2,2] = 22; for (i in a) print i } ' | cat -v
2^\2 1^\1 1^\2
多維陣列,在程式中可取得一個維度,其數量為該維度的個數。awk ' BEGIN { a[1][1] = 11; a[1][2] = 12; a[2][2] = 22; for (x in a) { print x for (y in a[x]) print "\t" y } } '
1 1 2 2 2
-
cat > sales.txt << EOF
sales,fruit,color,qty,amount
john,apple,red,1,100
john,banana,yellow,2,200
john,kiwi,green,3,300
kevin,lemon,yellow,1,100
marry,banana,yellow,2,200
marry,apple,green,3,300
EOF
awk -F, '
{
if (NR < 2) next
fruitAmount[$2] += $5
salesAmount[$1] += $5
}
END {
print "---------- Fruit amount"
for (fruit in fruitAmount) print fruit, fruitAmount[fruit]
print "---------- Sales amount"
for (sales in salesAmount) print sales, salesAmount[sales]
}
' sales.txt
-
if (NR < 2) next
目前行號 (NR
) 小於 2 時,執行下一行next
。 -
fruitAmount[$2] += $5
水果銷售陣列fruitAmount
其主鍵為水果欄位$2
,內容為累加銷售金額$5
。
寫成fruitAmount[$2] = fruitAmount[$2] + $5
也相同。 -
for (fruit in fruitAmount)
由for
取得fruitAmount
的每個主鍵fruit
(水果欄位)。 -
fruitAmount[fruit]
以主鍵fruit
取得fruitAmount
銷售金額內容值。
---------- Fruit amount apple 400 banana 400 lemon 100 kiwi 300 ---------- Sales amount kevin 100 marry 500 john 600
- 預先定義的陣列排序
-
陣列排序
@[ind|val]_[num|type|str]_[asc|desc]
選項-
@unsorted
無排序。 -
ind
按主鍵(索引)排序,val
按內容值排序。 -
num
嘗試以數字排序 (1a
會以1
排序),無前置數字在前。
str
按字串 (文字) 順序排序。
type
按類型排序,數字類型或類數字 (內容全部為數字) 在前,字串 (文字) 類型在後 (1a
為字串類型)。 -
asc
為升冪,desc
為降冪。
陣列排序組合列表@ind_num_asc
@ind_num_desc
@ind_str_asc
@ind_str_desc
@val_num_asc
@val_num_desc
@val_str_asc
@val_str_desc
@val_type_asc
@val_type_desc
註:沒有ind_type
的組合。使用方式PROCINFO["sorted_in"] = "@ind_str_asc" for (i in array) print i, array[i]
str
num
type
排序示例awk ' function sortdemo(format, arr) { PROCINFO["sorted_in"] = format (4) print format for (i in arr) { print typeof(arr[i]), arr[i] (5) } print "-------------" } BEGIN { split("31 5 red 1a", a) (1) a[100] = 400 (2) sortdemo("@val_str_asc", a) (3) sortdemo("@val_num_asc", a) sortdemo("@val_type_asc", a) } '
1 建立陣列 a
內容值為31
、5
、red
、1a
。2 陣列 a[100]
設定為數字 400。3 將排序方式 @val_str_asc
傳遞給sortdemo
的 format。4 sortdemo
設定排序方式 format。5 列印每個陣列的資料類型及內容 (awk 4.2 才有支援 typeof
函數)。@val_str_asc string 1a strnum 31 number 400 strnum 5 string red ------------- @val_num_asc string red string 1a strnum 5 strnum 31 number 400 ------------- @val_type_asc strnum 5 strnum 31 number 400 string 1a string red -------------
-
awk -F, '
{
if (NR < 2) next
fruitQty[$2,$3] += $4
fruitAmount[$2,$3] += $5
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for (combKey in fruitQty) {
split(combKey, sepKey, SUBSEP)
print sepKey[1], sepKey[2], fruitQty[sepKey[1], sepKey[2]], fruitAmount[sepKey[1], sepKey[2]]
}
}
' sales.txt
-
for (combKey in fruitQty)
由for
取得fruitQty
的每個主鍵combKey
。 -
split(combKey, sepKey, SUBSEP)
由於 (原始的)fruitQty
為多主鍵,combKey
也是多主鍵,執行split
以SUBSEP
(陣列分隔字元) 分割出每個主鍵儲存至陣列sepKey
中的sepKey[1]
、sepKey[2]
。 -
fruitQty[sepKey[1], sepKey[2]]
以主鍵sepKey[1]
、sepKey[2]
取得fruitQty
的內容值,fruitAmount
的主鍵跟fruitQty
相同,以相同的主鍵fruitAmount[sepKey[1], sepKey[2]]
取得fruitAmount
的內容值。
apple green 3 300 apple red 1 100 banana yellow 4 400 kiwi green 3 300 lemon yellow 1 100
asorti
示例)awk -F, '
{
if (NR < 2) next
fruitQty[$2,$3] += $4
fruitAmount[$2,$3] += $5
}
END {
asorti(fruitQty, sortKey)
for (i=1; i in sortKey; i++) {
split(sortKey[i], sepKey, SUBSEP)
print sepKey[1], sepKey[2], fruitQty[sepKey[1], sepKey[2]], fruitAmount[sepKey[1], sepKey[2]]
}
}
' sales.txt
執行結果跟前述的「水果銷售明細結果」相同。
-
asorti(fruitQty, sortKey)
將fruitQty
的主鍵排序後儲存至sortKey
其主鍵值由整數 1 開始。sortKey
陣列如下:1 apple^\green 2 apple^\red 3 banana^\yellow 4 kiwi^\green 5 lemon^\yellow
-
for (i=1; i in sortKey; i++)
由for
取得每個整數主鍵i
。for
的 condition 為i in sortKey
改為i <= length(sortKey)
也相同。for (i=1; i <= length(sortKey); i++)
-
split(sortKey[i], sepKey, SUBSEP)
sortKey[i]
為 (已排序的) 多主鍵的內容,執行split
以SUBSEP
(陣列分隔字元) 分割出每個主鍵儲存至陣列sepKey
中的sepKey[1]
、sepKey[2]
。 -
fruitQty[sepKey[1], sepKey[2]]
以主鍵sepKey[1]
、sepKey[2]
取得fruitQty
的內容值,fruitAmount
的主鍵跟fruitQty
相同,以相同的主鍵fruitAmount[sepKey[1], sepKey[2]]
取得fruitAmount
的內容值。
awk -F, '
{
if (NR < 2) next
fruitSales[$2][$3]["qty"] += $4
fruitSales[$2][$3]["amt"] += $5
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for (fruit in fruitSales) {
subqty = 0; subamt = 0;
for (color in fruitSales[fruit]) {
qty = fruitSales[fruit][color]["qty"]
amt = fruitSales[fruit][color]["amt"]
print fruit, color, qty, amt
subqty += qty; subamt += amt;
}
print "--------------------"
print fruit, "subtotal", subqty, subamt
print ""
}
}
' sales.txt
apple green 3 300 apple red 1 100 -------------------- apple subtotal 4 400 banana yellow 4 400 -------------------- banana subtotal 4 400 kiwi green 3 300 -------------------- kiwi subtotal 3 300 lemon yellow 1 100 -------------------- lemon subtotal 1 100
(
cat << EOF
fruit,color-list
apple,green,red
lemon,green,yellow
banana,yellow
kiwi,green,gold
grape,black,red,purple
EOF
) |
awk -F, '
{
if (NR < 2) next
for(i=2; i<=NF; i++)
array[$i][$1]
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
for (color in array) {
print color, length(array[color])
for (fruit in array[color]) {
print "\t" fruit
}
}
}
'
-
for(i=2; i<=NF; i++)
由for
取得 i,該值跳過第 1 欄位的水果,由 2 開始至該行的欄位數NF
,i 值為顏色欄位。 -
array[$i][$1]
收集多維陣列array
第 1 維度的主鍵為顏色欄位$i
,第 2 維度的主鍵為水果$1
。
註:只需收集主鍵值,不需要內容值。
black 1 grape gold 1 kiwi green 3 apple kiwi lemon purple 1 grape red 2 apple grape yellow 2 banana lemon