AWK教程
通俗的來講awk更像是一個行列處理器。
awk、sed、grep更適合的方向:
grep 更適合單純的查找或匹配文本
sed 更適合編輯匹配到的文本
awk 更適合格式化文本,對文本進行較複雜格式處理
基礎知識
讀(Read)
AWK 從輸入流(文件、管道或者標準輸入)中讀入一行然後將其存入記憶體中。
執行(Execute)
對於每一行輸入,所有的 AWK 命令按順執行。 默認情況下,AWK 命令是針對於每一行輸入,但是我們可以將其限制在指定的模式中。
重複(Repeate)
一直重複上述兩個過程直到文件結束。
開始塊 BEGIN BLOCK
BEGIN {awk-commands}
類似於java中的static塊,在程式啟動時執行,且整個過程只執行一次
一般用於初始化一些變數(如分隔符),BEGIN是AWK的關鍵詞,必須大寫。
開始塊部分是可選的,一個程式可以沒有開始塊部分。
主體塊 BODY BLOCK
/pattern/ {awk-commands}
如上方工作流中展示的那樣,每個輸入的行都會執行一次主體塊的命令。
注意,主體塊部分沒有關鍵字
結束塊 END BOLCK
END {awk-commands}
結束塊是在程式結束時執行的程式碼。 END 也是 AWK 的關鍵字,它也必須大寫。 與開始塊相似,結束塊也是可選的。
awk命令行
awk [option] file...
# 列印日誌文件
awk '{print}' info.log
$0
此變數表示整個輸入記錄。
[jerry]$ awk '{print $0}' marks.txt
#執行上面的命令可以得到如下的結果:
1) Amit Physics 80
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89
$n
此變數表示當前輸入記錄的第 n 個域,這些域之間由 FS 分割。
[jerry]$ awk '{print $3 "\t" $4}' marks.txt
執行上面的命令可以得到如下的結果:
Physics 80
Maths 90
Biology 87
English 85
History 89
AWK程式文件
除了使用命令外,awk還可以通過讀入文本文件中的命令執行
在test.awk文本中寫入{print}
執行awk命令
#列印日誌文件
awk -f test.awk info.log
基本命令
awk默認分隔符為空格或tab
元素下表從1開始
示例文本
# text
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
awk '{[pattern] action}' {filenames} # 行匹配語句 awk '' 只能用單引號
匹配文本某一列
awk '{print $1,$4}' log.txt #每行按空格或TAB分割,輸出文本中的1、4項
#結果
2 a
3 like
This's
10 orange,apple,mongo
# 格式化輸出
$ awk '{printf "%-8s %-10s\n",$1,$4}' log.txt
---------------------------------------------
2 a
3 like
This's
10 orange,apple,mongo
通過匹配模式輸出某一行
awk '/ABCacheLoader/ {print$0}' info.log
如果某行中含有/parttern/
中的字元串,則該行會被輸出。
如果沒有主體塊——默認的動作是輸出記錄(行)。因此上面的效果也可以使用下面簡略方式實現,它們會得到相同的結果:
awk '/ABCacheLoader/' info.log
通過匹配模式輸出某一列
前面我們已經看到了,當模式串匹配成功後, AWK 默認會輸出整個記錄。不過,我們可以讓 AWK 只輸出特定的域(列)的內容。 例如,下面的這個例子中當模式串匹配成功後只會輸出第1列與第3列的內容:
awk '/ABCacheLoader/ {print$1 "\t" $3}' info.log
其中$1
和$3
的順序可以隨意地調換,即我們能以任意順序輸出各列
計算匹配次數並輸出
統計模式串成功匹配文件中行的次數
awk '/OfflineTaskCacheLoader/{++cnt}END{print "count=",cnt}' info.log
上面這個例子中,每次成功的匹配我們都會增加計數器的值,並在結束塊中將該計數器的值輸出。 請注意,與其它程式語言不一樣的地方在於, AWK 在使用一個變數前不需要特意地聲明這個變數。
輸出字元數大於18的行
awk 'length($0)>18' info.log
AWK 提供了內置的 length 函數。該函數返回字元串的長度。變數 $0 表示整行,缺失的主體塊會執行默認動作,例如,列印輸出。 因此,如果一行中字元數超過 18, 則比較的結果為真,該行則被輸出。
內置變數
ARGC 參數個數
ARGC 表示在命令行提供的參數的個數。
awk 'BEGIN {print "argc = " ,ARGC}' info.log info.log
argc = 3
ARGV 輸入參數的數組
這個變數表示存儲命令行輸入參數的數組。數組的有效索引是從 1 到 ARGC
。其中ARGV[0] = awk
awk 'BEGIN {for (i=0; i<ARGC;i++) {printf "ARGV[%d] = %s\n",i,ARGV[i]}}' one two three
ARGV[0] = awk
ARGV[1] = one
ARGV[2] = two
ARGV[3] = three
ENVIRON 環境變數
獲取環境變數
具體的環境變數可以通過系統中的 env 命令查詢其它環境變數的名字
awk 'BEGIN{print ENVIRON["PATH"]}'
FILENAME 文件名
值得注意的是在開始塊中FILENAME是未定義的
awk 'END{print FILENAME}' info.log
info.log
FS (Field Separator 欄位分隔符)
此變數表示輸入的數據域之間的分隔符,其默認值是空格。 你可以使用 -F 命令行選項 或者在 BEGIN塊中設置分割符 改變它的默認值。
awk 'BEGIN{FS=":"}{print $2}' info.log
#上下等價
awk -F ":" '{print $2}' info.log
awk 'BEGIN{FS=":";print "FS ="FS}' info.log
FS =:
FS和OFS命令
FS:Field Separator,欄位分隔符(對輸入生效)
OFS:Out of Field Separator,輸出欄位分隔符(對輸出生效)
awk 'BEGIN{ FS=" ";OFS="," }{ print $1,$2,$3,$4 }' file2.txt > file3.txt
NF(Number of Filed)
此變數表示當前輸入記錄中域的數量。例如,下面這個例子只輸出超過兩個域的行:
echo -e 激活轉義字元。使用-e選項時,若字元串中出現轉義字元,則特別加以處理,不會當做一般文字輸出
echo -e "one two\nthree\none two three" | awk 'NF >2'
one two three
NR(Number of Row)
該變數記錄當前處理的記錄的數量。下面例子會輸出讀入的前2行(NR<3)。
cat info.log|awk 'NR < 3'
#上下等價
awk '{if(NR<3){print}}' info.log
FNR(File Number of Row)
該變數與 NR 類似,不過它是相對於當前文件而言的。此變數在處理多個文件輸入時有重要的作用。每當從新的文件中讀入時 FNR 都會被重新設置為 0。
awk '{print NR,FNR}' info.log newtable
# 新文件NR繼續計數,而FNR重0開始計數
1029 1029
1030 1030
1031 1
1032 2
OFS(Output Field Separator 輸出欄位分割符)
此變數表示輸出域之間的分割符,其默認為空格。
對 {print $1,$2,$3}生效 ,即只對print中的逗號(,)生效
awk 'BEGIN{OFS = "---"}{print $1,$2}' info.log
-----
2020-06-15---20:00:02.446
2020-06-15---20:00:02.457
2020-06-15---20:00:02.462
RS (Row Separator 輸入行分隔符)
此變數表示輸入記錄(行)之間的分隔符,默認為換行符
#以:作為行分割
awk 'BEGIN{RS=":"}{print $1}' info.log | more
-------
2020-06-15
00
02.446
]
49]
00
02.457
]
67]
ORS(行分隔符)
此變數表示輸出記錄(行)之間的分割符,其默認值是換行符。
awk 'BEGIN{RS=":";ORS="---"}{print $1}' info.log | more
2020-06-15---00---02.446---]---49]---00---02.457---]---67]---00---02.462---]---56]---00---02.468---]
RSTART(匹配字元串)
此變數表示由 match 函數匹配的字元串的第一個字元的位置。
awk 'BEGIN { if (match("One Two Three", "Thre")) { print RSTART } }'
#執行上面的命令可以得到如下的結果:
9
操作符
正則表達式操作符
下面將介紹兩種正則表達式操作符:
匹配(Match)
匹配運算符為~。它用於搜索包含匹配模式字元串的域。下面的示例中將輸出包括 9 的行:
[jerry]$ awk '$0 ~ 9' marks.txt
執行上面的命令可以得到如下的結果:
2) Rahul Maths 90
5) Hari History 89
不匹配(Not match)
不匹配操作符為 !~。 此操作符用於搜索不匹配指定字元串的域。如下示例輸出不包含 9 的行:
[jerry]$ awk '$0 !~ 9' marks.txt
執行上面的命令可以得到如下的結果:
1) Amit Physics 80
3) Shyam Biology 87
4) Kedar English 85
點(Dot)
點字元(.)可以匹配除了行結束字元的所有字元。比如下面的便子就可以匹配 fin, fun, fan 等等。
echo -e "cat\nbat\nfun\nfin\nfan" | awk '/f.n/'
#輸出
fun
fin
fan
行開始
行開始符(^)匹配一行的開始。下面的示例將輸出所有以字元串 The 開始的行。
echo -e "this is lipengtest" | awk '/^t/'
this is lipengtest
行結束
行結束符($)匹配一行的結束。下面的例子中將輸出所有以字元 t 結束的行:
echo -e "this is lipengtest" | awk '/t$/'
this is lipengtest
匹配字符集
匹配字符集用於匹配集合(由方括弧表示)中的一個字元。如下例子中,匹配 Call 與 Tall 而不會匹配 Ball。
echo -e "Call\nTall\nBall" | awk '/[CT]all/'
執行上面的命令可以得到如下結果:
fun
fin
fan
排除集
正則匹配時會排除集合中的字元。如下例子中只會輸出 Ball。
注意,^符號只有在集合內才會起作用,且如果放在開頭作用範圍為整個集合
#放在集合開頭
echo -e "Call\nTall\nBall" | awk '/[^CT]all/'
執行上面的命令可以得到如下結果:
Ball
#放在集合中間
echo -e "Call\nTall\nBall" | awk '/[C^T]all/'
Call
Tall
#放在集合外,用作行開始
echo -e "Call\nTall\nBall" | awk '/^[CT]all/'
Call
Tall
或
豎線(|)允許正則表達式實現邏輯或運算. 下面例子將會輸出 Ball 與 Call 。
echo -e "Call\nTall\nBall\nSmall\nShall" | awk '/Call|Ball/'
執行上面的命令可以得到如下結果:
Call
Ball
最多出現一次
該符號(?)前面的字元不出現或者出現一次
。如下示例匹配 Colour 與 Color。 使用 ? 使得 u 變成了可選字元 。
echo -e "Colour\nColor" | awk '/Colou?r/'
執行上面的命令可以得到如下結果:
Colour
Color
分組
括弧用於分組而字元 | 用於提供多種選擇。如下的正則表達式會匹配所有包含 Apple Juice 或 Aplle Cake 的行。
[jerry]$ echo -e "Apple Juice\nApple Pie\nApple Tart\nApple Cake" | awk '/Apple (Juice|Cake)/'
#這裡的空格是會算進表達式里的
#執行上面的命令可以得到如下結果:
Apple Juice
Apple Cake
控制流
IF-ELSE語句
if-else語句中允許在條件為假時執行另外一組的動作。下面為 if-else 的語法格式:
if (condition)
action-1
else
action-2
其中,條件為真時執行 action-1,條件為假時執行 action-2。下面是使用該語句判斷數字是否為偶數的例子:
awk 'BEGIN {num = 11;
if (num % 2 == 0) printf "%d is even number.\n", num;
else printf "%d is odd number.\n", num
}'
執行上面的操作可以得到如下的結果:
11 is odd number.
EXIT
Exit 用於結束腳本程式的執行。該函數接受一個整數作為參數表示 AWK 進程結束狀態。 如果沒有提供該參數,其默認狀態為 0 。下面例子中當和大於 50 時結束 AWK 程式。
awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) exit(10); else print "Sum =", sum
}
}'
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45
讓我們檢查一下腳本執行後的返回狀態:
echo $?
執行上面的命令可以得到如下的結果:
19
實際例子
篩選出第n個列並輸出到文件中
awk 'BEGIN{FS=";"}{print $1 >> "result1.sql"}' result.txt
傳入參數並根據參數匹配輸出
#!/bin/bash
#########
#
# 目標:查找文件中對應的sql和riskey
# author: lipeng01_dxm
#
#########
# host="10.21.181.103"
# Port=8888
# user="db_user_rw"
# password="EDCIdek25"
# database="mask"
riskey_array=(
"1"
"2"
"3"
"4"
"5"
"6"
)
shell_var="abc"
for ((i=0; i<6; i++)); do
shell_var=${riskey_array[i]}
# awk 'BEGIN{riskey="'$riskey_array[i]'";if($2 == riskey) printf $2}'
awk 'BEGIN{riskey = "'$shell_var'";}{if($2 == riskey) print $0 >> "/Users/dxm/Desktop/result.txt"}' /Users/dxm/Desktop/rieskeyallsql.txt
done
匹配模式輸出
awk 'BEGIN{FS=";"}{if($1!~/mdm_per_detail_user_info/) {print "--\n" $1 >> /Users/dxm/Desktop/result2.sql}}' result.txt
文件對比
#在awk中,FNR指的是當前文件中的記錄號(通常是行號)並NR引用總記錄號。運算符==是一個比較運算符,當兩個周圍的操作數相等時返回true。
#這意味著條件NR==FNR僅適用於第一個文件,因為FNR每個文件的第一行重置為1,但NR會繼續增加。
#此模式通常用於僅對第一個文件執行操作。在next塊內是指任何進一步的命令被跳過,所以它們僅在比所述第一其他文件運行。
#該條件FNR==NR比較了相同的兩個操作數NR==FNR,因此它的行為方式相同。
awk -F',' 'NR==FNR{a[$1]=$1;next}NR!=FNR{if($1 in a){}else{print $0}}' 5568.csv 5569.csv
awk 'BEGIN{FS = ","}{if(NR==FNR){a[$1]=$1}else{if($1 in a){}else{print $0}}}' 2370.csv 5568.csv
curl命令轉換
awk '{print "curl -L -X POST \047//10.157.23.145:8307/mask/_doc/" $1 "\047 -H \047Authorization: Basic ZWxhc3RpYzptYXNrX3Jk\047 -H \047Content-Type: application/json\047 --data-raw \047{}\047"}' 2020-08-12-01.txt > ./2020-08-12-01.sh
sh ./2020-08-12-01.sh
單引號因為是關鍵字,所以需要使用8進位的\047來代替