1. awk 教程
1.1 調用 awk
awk 是一種強大的文本處理工具,在 Linux 系統中廣泛應用于日志分析、數據處理等場景。調用 awk 主要有以下三種方式:
1.1.1 命令行方式
基本語法為:
awk (-F filed-separator) 'commands' input-files
其中,-F
用于指定分隔符,默認情況下,awk 以空格或制表符作為分隔符。commands
是 awk 的命令,input-files
則是要處理的文件。例如,我們有一個文件data.txt
,內容如下:
apple 3 1.5
banana 5 2.0
cherry 2 1.8
如果我們想以空格為分隔符,打印每一行的第一個和第三個字段,可以使用以下命令:
awk '{print $1,$3}' data.txt
輸出結果為:
apple 1.5
banana 2.0
cherry 1.8
如果文件中的字段是以冒號分隔,我們就需要使用-F
選項指定分隔符。比如有一個users.txt
文件,內容為:
user1:password1:1001
user2:password2:1002
user3:password3:1003
要打印每個用戶的用戶名(第一個字段)和用戶 ID(第三個字段),命令如下:
awk -F: '{print $1,$3}' users.txt
輸出:
user1 1001
user2 1002
user3 1003
注意:
?單引號雙引號都行,但是要按照規范
場景 | 單引號(') | 雙引號(") | |
---|---|---|---|
純正則匹配 | 推薦使用,語法簡潔,避免 Shell 干擾 | 也可使用,但需注意轉義 | |
包含 Shell 變量 | 無法直接使用,需額外處理 | 直接引用變量,Shell 先解析 | |
正則包含特殊字符 | 自動防止 Shell 解析,直接使用 | 需用反斜杠轉義(如\$ 、\+ ) | |
避免引號嵌套錯誤 | 適合正則中無單引號的場景 | 適合正則中包含單引號的場景 |
1.1.2 腳本文件可執行方式
將所有 awk 命令插入一個文件,并使該文件可執行。同時,在腳本文件的首行使用#!/usr/bin/awk -f
,這樣就可以通過直接鍵入腳本名稱來調用它。
例如,創建一個名為process.awk
的文件,內容如下:
#!/bin/awk -f
{print $2}
然后為該文件添加可執行權限:
chmod +x process.awk
假設有一個input.txt
文件,內容為:
one two three
four five six
seven eight nine
執行process.awk
腳本處理input.txt
文件:
./process.awk input.txt
輸出結果為:
two
five
eight
如果想要改變分隔符:
#!/bin/awk -f
# 設置字段分隔符為冒號
BEGIN { FS = ":" } #FS 的英文全稱是Field Separator
{ print $1, $3 }
1.1.3 調用外部腳本方式
將所有的 awk 命令插入一個單獨文件,然后使用-f
選項調用該腳本。語法為:
awk -f awk-script-file input-files
例如,有一個腳本文件calculate.awk
,內容如下:
{
sum = $1 + $2
print sum
}
我們有一個數據文件numbers.txt
,內容為:
3 5
2 7
4 6
執行命令:
awk -f calculate.awk numbers.txt
輸出:
8
9
10
1.2 awk 腳本
1.2.1 模式和動作
任何 awk 語句都由模式和動作組成。模式部分決定動作語句何時觸發及觸發條件,而動作則是對數據進行的具體操作。如果省略模式部分,動作將對每一行輸入數據都執行。
模式可以是條件語句、復合語句或正則表達式,其中有兩個特殊字段BEGIN
和END
。BEGIN
語句用于設置計數、打印表頭之類的操作,它會在任何文本瀏覽動作之前執行。END
語句則在 awk 完成對所有輸入文本的瀏覽動作后執行,通常用于打印輸出文本總數、結尾狀態標志等。
動作通常放在大括號{}
內。動作最常見的是打印操作,但也可以包含更長的代碼,如if
語句、循環語句及循環退出結構等。如果不指明動作,awk 將默認打印出所有輸入記錄。
例如,我們要處理一個成績文件grades.txt
,內容如下:
Alice 85 90 78
Bob 70 65 80
Charlie 95 88 92
我們想在處理文件前打印表頭,處理文件時打印每個學生的姓名和平均成績,處理完后打印一個結束信息。可以使用以下 awk 腳本:
awk
BEGIN {
print "Student\tAverage Grade"
print "----------------------"
}
{
avg = ($2 + $3 + $4) / 3
print $1 "\t" avg
}
END {
print "----------------------"
print "End of Grades Report"
}
執行該腳本:
awk -f script.awk grades.txtsource files / command-line arguments must contain complete functions or rules
出現這個錯誤可能是你的大括號未閉合
輸出:
Student Average Grade
----------------------
Alice 84.3333
Bob 71.6667
Charlie 91.6667
----------------------
End of Grades Report
1.2.2 域和記錄
awk 執行時,會將輸入的每一行視為一條記錄,并且將記錄中的各個部分(以分隔符分隔)標記為$1
、$2
……$n
,這種方式稱為域標識。當需要指定多個域時,使用逗號,
進行分隔,如$1,$3
指定的是第一個域和第三個域。如果希望指定整行記錄,則可以使用$0
。
使用print
命令執行打印操作,該命令需要用{}
括起來。
1.2.2.1 抽取域
以之前的grades.txt
文件為例,我們要打印每個學生的姓名(第一個域)和數學成績(第二個域),命令如下:
awk '{print $1,$2}' grades.txt
輸出:
Alice 85
Bob 70
Charlie 95
1.2.2.2 保存 awk 輸出
保存 awk 輸出結果主要有兩種方式:
- 重定向到文件:這種方式下,屏幕不會顯示輸出內容,而是將結果保存到指定文件中。例如,將
grades.txt
文件中每個學生的姓名保存到students.txt
文件中,命令為:
awk '{print $1}' grades.txt > students.txt
- 使用管道將輸出結果傳給
tee
:tee
命令可以將輸出同時顯示在屏幕上并保存到文件中。例如,將grades.txt
文件中每個學生的總成績(三個成績之和)輸出到屏幕并保存到total_grades.txt
文件中,命令如下:
awk '{sum=$2+$3+$4; print sum}' grades.txt | tee total_grades.txttee命令不可用的話,下載
yum install coreutils
1.2.2.3 使用標準輸入
可以通過多種方法將標準輸入作為 awk 的輸入源:
- 直接在命令后跟上文件名,如
awk '{print $0}' grades.txt
,這里grades.txt
的內容會作為標準輸入被 awk 讀取處理。 - 使用
<
符號指定輸入文件,如awk '{print $0}' < grades.txt
,效果與上一種方法相同。
1.2.2.4 打印所有記錄
要打印輸入文件的所有記錄,使用以下命令:
awk '{print $0}' input_file
其中input_file
是要處理的文件名。
1.2.2.5 打印單獨記錄
只打印特定的域,如之前提到的只打印$1
和$4
:
awk '{print $1,$4}' input_file
1.2.2.6 自定義格式打印
我們可以在輸出內容中添加注釋、自定義分隔符等。例如,對于grades.txt
文件,我們要在輸出學生姓名和平均成績時,在上方添加注釋 “Student Name” 和 “Average Score”,并使用制表符分隔,命令如下:
awk 'BEGIN {print "Student Name\tAverage Score\n------------------------------"} {avg = ($2 + $3 + $4) / 3; print $1 "\t" avg}' grades.txt
輸出:
Student Name Average Score
------------------------------
Alice 84.3333
Bob 71.6667
Charlie 91.6667
1.2.2.7 awk 錯誤信息提示
在使用 awk 時,如果遇到錯誤,可以從以下幾個方面排查:
- 確保整個 awk 命令用單引號括起來,因為雙引號在 Shell 中可能會導致變量提前展開等問題,影響 awk 命令的執行。
- 確保命令內所有引號成對出現,無論是單引號還是雙引號。
- 確保用大括號
{}
括起動作語句,用括號()
括起條件語句。 - 檢查是否遺漏了大括號
{}
,尤其是在包含多個動作或復雜條件判斷時。
1.2.2.8 awk 鍵盤輸入
如果在執行 awk 命令時沒有指定輸入文件,awk 會從鍵盤讀取輸入。輸入完成后,按Ctrl + D
組合鍵結束輸入。例如,執行awk '{print $0}'
,然后在命令行輸入一些文本,每輸入一行按回車鍵,輸入完成后按Ctrl + D
,awk 會打印出你輸入的每一行內容。
1.2.3 元字符
awk 支持一些元字符,用于模式匹配等操作,常見的元字符有\
、^
、$
、.
、(
、)
、|
、*
、+
、?
。其中+
和?
在 grep 或 sed 中可能有不同的行為,但在 awk 中有其特定的含義:
+
:匹配一個或一個以上前面的單字符。例如,/XY+Z/
可以匹配XYZ
、XYYYYZ
等。?
:匹配 0 個或一個前面的單字符。例如,/XY?Z/
可以匹配XYZ
、XZ
。
例如,有一個文件strings.txt
,內容為:
XZY
XYZY
XYYYYZY
XZ
要匹配包含XY
后面跟著一個或多個Y
再跟著Z
的字符串,可以使用以下命令:
awk '/XY+Z/' strings.txt
輸出:
XYZY
XYYYYZY
[root@free ~]# awk '/XY?Z/' string.txt
XZY
XYZY
XZ
1.2.4 條件操作符
awk 支持多種條件操作符,用于條件判斷:
操作符 | 描述 |
---|---|
< | 小于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
~ | 匹配正則表達式 |
!~ | 不匹配正則表達式 |
1.2.4.1 匹配
使用~
緊跟正則表達式可以匹配域,也可以使用if
語句進行條件判斷,條件需要用()
括起來。
例如,有一個文件employees.txt
,內容如下:
John,Manager,50000
Alice,Engineer,45000
Bob,Engineer,48000
要查詢職位是工程師(Engineer
)的員工信息(打印出$2
匹配Engineer
的行),可以使用以下兩種方式:
awk '{if ($2~/Engineer/) print $0}' employees.txt為什么不需要分號?
因為 if 語句控制下的 print 是同一個邏輯塊的一部分,不是獨立的語句。正則表達式的基本語法:
在 awk 中,正則表達式通常用 /pattern/ 表示,其中:
開頭的 /:標記正則表達式的開始;
結尾的 /:標記正則表達式的結束;
中間的 pattern:是具體的匹配模式(如 Engineer)。
或者
awk '$2 ~ /Engineer/' employees.txt
1.2.4.2 精確匹配
使用==
并用雙引號括起條件可以進行精確匹配。例如,要查詢薪資為48000
的員工信息:
awk '{if ($3 == "48000") print $0}' employees.txt
1.2.4.3 不匹配
使用!~
緊跟正則表達式可以實現不匹配域的操作。例如,要查詢職位不是經理(Manager
)的員工信息:
awk '{if ($2!~/Manager/) print $0}' employees.txt
或者
awk '$2 !~ /Manager/' employees.txt
1.2.4.4 比較
以比較薪資大小為例,要找出薪資大于45000
的員工姓名和薪資:
awk '{if ($3 > 45000) print $1,$3}' employees.txt
1.2.4.5 各種匹配
- 匹配
Green
或green
:
awk '/(G|g)reen/' input_file
- 匹配
$1
的第四個字符是a
:
awk '$1 ~ /^...a/' input_file
- 匹配
Yellow
或Brown
:
awk '$4 ~ /Yellow|Brown/' input_file
- 匹配以
J
開頭的行:
awk '$0 ~ /^J/' input_file
1.2.4.6 復合表達式
復合模式或復合操作符用于形成復雜的邏輯操作,復合表達式即為模式間通過使用復合操作符互相結合起來的表達式。常用的復合操作符有&&
(邏輯與)和||
(邏輯或):
&&
(AND):符號兩邊的條件必須同時為真。例如,要查詢職位是工程師且薪資大于45000
的員工信息:
awk '{if ($2 == "Engineer" && $3 > 45000) print $0}' employees.txt
||
(OR):符號兩邊的條件只要有一個為真即可。例如,要查詢職位是經理或者薪資大于48000
的員工信息:
awk '{if ($2 == "Manager" || $3 > 48000) print $0}' employees.txt
1.2.5 awk 內置變量
awk 有許多內置變量,用于設置環境信息、獲取輸入輸出相關的狀態等。以下是一些常用的內置變量:
ARGC
:表示命令行參數的個數。例如,執行awk -v var1=value1 -v var2=value2 -f script.awk file1 file2
,ARGC
的值為5
(包括awk
命令本身、兩個-v
選項及兩個文件名)。ARGV
:是一個數組,存儲命令行參數的排列。ARGV[0]
通常是awk
命令本身,ARGV[1]
及后續元素為命令行參數。FNR
:與NR
類似,用于記錄輸入的行數。不同之處在于,FNR
在處理多個文件時,對每個文件都會從1
開始計數,而NR
是對所有輸入文件的總行數進行計數。例如,有兩個文件file1.txt
和file2.txt
,在處理file1.txt
時,FNR
和NR
從1
開始遞增,當處理完file1.txt
開始處理file2.txt
時,NR
繼續遞增,而FNR
又從1
開始。FS
:用于指定輸入字段的分隔符。可以在BEGIN
塊中定義,也可以使用-F
選項在命令行指定。例如,BEGIN {FS=":"}
將輸入字段分隔符設置為冒號。RS
:輸入的記錄分隔符,默認是換行符,即文本按一行一行輸入。可以修改這個變量來改變記錄的分隔方式。例如,BEGIN {RS="\n\n"}
可以將連續兩個換行符作為記錄分隔符,適用于處理段落等以空行分隔的文本。OFS
:輸出字段分隔符,默認是空格。可以修改為其他字符,如制表符\t
或逗號,
等。例如,BEGIN {OFS="|"}
會將輸出的字段用豎線分隔。ORS
:輸出的記錄分隔符,默認為換行符,即處理結果也是一行一行輸出到屏幕。可以修改為其他字符,如BEGIN {ORS=","}
會將輸出的記錄用逗號分隔,最后一個記錄后也會有逗號。
例如,我們要統計一個文件中每行的字段數,并輸出行號、每行內容及字段數,使用以下腳本:
{
print NR, $0, NF
}
這里NR
表示行號,NF
表示當前行的字段數。假設文件example.txt
內容為:
apple banana cherry
dog cat mouse
執行awk '{print NR, $0, NF}' example.txt
,輸出:
1 apple banana cherry 3
2 dog cat mouse 3
再如,我們想以冒號為輸入分隔符,以逗號為輸出分隔符,打印文件data.csv
的第一和第三個字段:
BEGIN {
FS=":"
OFS=","
}
{
print $1,$3
}
假設data.csv
內容為:
1:apple:red
2:banana:yellow
3:grape:purple
執行該腳本,輸出:
1,red
2,yellow
3,purple