1. awk工作模式介紹
1.1. 簡介
- awk是一個文本處理工具,通常用于處理資料并生成結果報告;
- awk的命名是它的創始人 Alfred Aho、Peter Weinberger和Brian Kernighan 姓氏的首個字母組成的;
1.2. 文法格式
- 第一種形式:awk ‘BEGIN{}pattern{commands}END{}’ file_name
- 第二種形式:standard output | awk ‘BEGIN{}pattern{commands}END{}’
文法格式說明
文法格式 | 解釋 |
---|---|
BEGIN{} | 正式處理資料之前執行 |
pattern | 比對模式 |
{commands} | 處理指令,可能多行 |
END{} | 處理完所有比對資料後執行 |
2. awk的内置變量
2.1. 内置變量對照表
内置變量 | 含義 |
---|---|
$0 | 整行内容 |
1 − 1- 1−n | 目前行的第1-n個字段 |
NF(Number Field) | 目前行的字段個數,也就是有多少列 |
NR(Number Row) | 目前行的行号,從1開始計數 |
FNR(File Number Row) | 多檔案處理時,每個檔案行号單獨計數,都是從0開始 |
FS(Field Separator) | 輸入字段分隔符。不指定則預設以空格或tab分割 |
RS(Row Separator) | 輸入行分隔符。預設回車換行\n |
OFS(Output Filed Separator) | 輸出字段分隔符。預設為空格 |
ORS(Output Row Separator) | 輸出行分隔符,預設為回車換行 |
FILENAME | 目前輸入的檔案名字 |
ARGC | 指令行參數個數 |
ARGV | 指令行參數數組 |
3. awk格式化輸出之printf
3.1. printf的格式說明符
格式符 | 含義 |
---|---|
%s | 列印字元串 |
%d | 列印十進制 |
%f | 列印一個浮點數 |
%x | 列印十六進制 |
%o | 列印八進制 |
%e | 列印數字的科學計數法形式 |
%c | 列印單個字元的ASCII碼 |
格式符示例:
- 以字元串格式列印/etc/passwd中的第7個字段,以“:”作為分隔符;
awk 'BEGIN{FS=":"} {printf "%s",$7}' /etc/passwd
- 以10進制格式列印/etc/passwd中的第3個字段,以“:”作為分隔符;
awk 'BEGIN{FS=":"} {printf "%d\n",$3}' /etc/passwd
- 以浮點數格式列印/etc/passwd中的第3個字段,以“:”作為分隔符;
awk 'BEGIN{FS=":"} {printf "%f\n,$3"}' /etc/passwd
- 以16進制數格式列印/etc/passwd中的第3個字段,以“:”作為分隔符;
awk 'BEGIN{FS=":"} {printf "%#x\n,$3"}' /etc/passwd
- 以8進制數格式列印/etc/passwd中的第3個字段,以“:”作為分隔符;
awk 'BEGIN{FS=":"} {printf "%#o\n,$3"}' /etc/passwd
- 以科學計數法格式列印/etc/passwd中的第3個字段,以“:”作為分隔符;
awk 'BEGIN{FS=":"} {printf "%e\n,$3"}' /etc/passwd
3.2. printf的修飾符
修飾符 | 含義 |
---|---|
- | 左對齊 |
+ | 右對齊 |
# | 顯示8進制在前面加0,顯示16進制在前面加0x |
修飾符示例:
- 左對齊格式
- 右對齊格式
- 列印8進制或16進制數字是在前面加#
4. awk模式比對的兩種用法
4.1. 模式比對的兩種用法
- 第一種模式比對:RegExp
- 第二種模式比對:關系運算比對
4.2. 用法格式對照表
文法格式 | 含義 |
---|---|
RegExp | 按正規表達式比對 |
關系運算 | 按關系運算比對 |
1. RegExp
- 比對/etc/passwd檔案行中含有root字元串的所有行;
awk 'BEGIN{FS=":"}/root/{print $0}' /etc/passwd
- 比對/etc/passwd檔案行中以yarn開頭的所有行;
awk 'BEGIN{FS=":"}/^yarn/{print $0}' /etc/passwd
2. 運算符比對
關系運算符比對:
< 小于
> 大于
<= 小于等于
>= 大于等于
== 等于
!= 不等于
~ 比對正規表達式
!~ 不比對正規表達式
-
- 以:為分隔符,比對/etc/passwd檔案中的第3個字段小于50的所有行資訊;
awk 'BEGIN{FS=":"}$3<50{print $0}' /etc/passwd
-
- 以:為分隔符,比對/etc/passwd檔案中的第3個字段大于50的所有行資訊;
awk 'BEGIN{FS=":"}$3>50{print $0}' /etc/passwd
-
- 以:為分隔符,比對/etc/passwd檔案中的第7個字段為/bin/bash的所有行資訊;
awk 'BEGIN{FS=":"}$7=="/bin/bash"{print $0}' /etc/passwd
-
- 以:為分隔符,比對/etc/passwd檔案中第7個字段不為/bin/bash的所有行資訊;
awk 'BEGIN{FS=":"}$7!="/bin/bash"{print $0}' /etc/passwd
-
- 以:為分隔符,比對/etc/passwd中第3個字段包含3個以上數字的所有行資訊;
awk 'BEGIN{FS=":"}$3~/[0-9]{3,}/{print $0}' /etc/passwd
布爾運算符比對:
|| 或
&& 與
! 非
-
- 以:為分隔符,比對/etc/passwd檔案中包含hdfs或yarn的所有行資訊;
awk 'BEGIN{FS=":"}$1=="hdfs" || $1=="yarn" {print $0}' /etc/passwd
-
- 以:為分隔符,比對/etc/passwd檔案中第3個字段小于50并且第4個字段大于50的所有行資訊;
awk 'BEGIN{FS=":"}$3<50 && $4>50 {print $0}' /etc/passwd
5. awk動作中的表達式用法
5.1. awk動作表達式中的算術運算符
運算符 | 含義 |
---|---|
+ | 加 |
- | 減 |
* | 乘 |
/ | 除 |
% | 模 |
^或** | 乘方 |
++x | 在傳回x變量之前,x變量加1 |
x++ | 在傳回x變量之後,x變量加1 |
–x | 在傳回x變量之前,x變量減1 |
x– | 在傳回x變量之後,x變量減1 |
- 使用awk計算/etc/services中的空白行數量;
awk '/^$/{sum++}END{print sum}' /etc/services
- 計算學生課程分數平均值,學生課程檔案内容如下:
Allen 80 90 96 98
Mike 93 98 92 91
Zhang 78 76 87 92
Jerry 86 89 68 92
Han 85 95 75 90
Li 78 88 98 100
#輸出格式
Allen 80 90 96 98 計算後的平均值
awk 'BEGIN{printf "%-8s%-8s%-8s%-8s%-8s%s\n","Name","Chinese","Math","English","Pysical","Average"}{total=$2+$3+$4+$5;AVG=total/4;printf "%-8s%-8d%-8d%-8d%-8d%0.2f\n",$1,$2,$3,$4,$5,AVG}' student.txt
6. awk動作中的條件及循環語句
6.1. 條件語句
if(條件表達式)
動作1
else if(條件表達式)
動作2
else
動作3
6.2. 循環語句-while
while
while(條件表達式)
動作
6.3. 循環語句-do while
do while
do
動作
while(條件表達式)
6.4. 循環語句-for
for
for(初始化計數器;測試計數器;計數器變更)
動作
- 以:為分隔符,隻列印/etc/passwd中第3個字段的數值在50-100範圍内的行資訊;
vim scrips.awk
BEGIN{
FS=":"
}
{
if($3<50)
{
printf "%-20s%-25s%-5d\n","UID<50",$1,$3
}
else if($3>50 && $3<100)
{
printf "%-20s%-25s%-5d\n","50<UID<100",$1,$3
}
else
{
printf "%-20s%-25s%-5d\n","UID>100",$1,$3
}
}
#運作scrips.awk腳本檔案
awk -f scrips.awk /etc/passwd
- 計算下列同學的平均分數,并且隻列印平均分數大于90的同學姓名和分數資訊(student.txt);
Name Chinese English Math Physical Average
Allen 80 90 96 98
Mike 93 98 92 91
Zhang 78 76 87 92
Jerry 86 89 68 92
Han 85 95 75 90
Li 78 88 98 100
vim student.awk
BEGIN{
printf "%-10s%-10s%-10s%-10s%-10s%-10s\n","Name","Chinese","English","Math","Physical","Average"
}
{
total=$2+$3+$4+$5
avg=total/4
if(avg>90)
{
printf "%-10s%-10d%-10d%-10d%-10d%-0.2f\n",$1,$2,$3,$4,$5,avg
}
}
#腳本檔案執行指令
awk -f student.awk student.txt
#在以上需求上并且求各科成績的和
vim student.awk
BEGIN{
printf "%-10s%-10s%-10s%-10s%-10s%-10s\n","Name","Chinese","English","Math","Physical","Average"
}
{
total=$2+$3+$4+$5
avg=total/4
if(avg>90)
{
printf "%-10s%-10d%-10d%-10d%-10d%-0.2f\n",$1,$2,$3,$4,$5,avg
score_chinese+=$2
score_english+=$3
score_math+=$4
score_physical+=$5
}
}
END{
printf "%-10s%-10d%-10d%-10d%-10d\n","",score_chinese,score_english,score_math,score_physical
}
#執行腳本檔案指令
awk -f student.awk student.txt
- 計算1+2+3+4+…+100的和,請使用while、do while、for三種弄循環方式實作;
#while循環方式實作
vim while.awk
BEGIN{
while(i<=100){
sum+=i
i++
}
print sum
}
#腳本檔案執行指令
awk -f while.awk
#for循環方式實作
vim for.awk
BEGIN{
for (i=0;i<=100;i++)
{
sum+=i
}
print sum
}
#腳本檔案執行指令
awk -f for.awk
#do-while循環方式實作
vim do-while.awk
BEGIN{
do
{
sum+=i
i++
}while(i<=100)
print sum
}
#腳本檔案執行指令
awk -f do-while.awk
7. awk中的字元串函數
7.1. 字元串函數對照表
函數名 | 解釋 | 函數傳回值 |
---|---|---|
length(str) | 計算字元串長度 | 整數長度值 |
index(str1,str2) | 在str1中查找str2的位置 | 傳回值為位置索引,從1計數 |
tolower(str) | 轉換為小寫 | 轉換後的小寫字元串 |
toupper(str) | 轉換為大寫 | 轉換後的大寫字元串 |
substr(str,m,n) | 從str的m個字元開始,截取n位 | 截取後的子串 |
split(str,arr,fs) | 按fs切割字元串,結果儲存arr | 切割後的子串的個數 |
match(str,RE) | 在str中按照RE查找,傳回位置 | 傳回索引位置 |
sub(RE,RepStr,str) | 在str中搜尋符合RE的子串,将其替換為RepStr;隻替換第一個 | 替換的個數 |
gsub(RE,RepStr,str) | 在str中搜尋符合RE的子串,将其替換為RepStr;替換所有 | 替換的個數 |
- 以:為分隔符,傳回/etc/passwd中每行中每個字段的長度;
#需處理的檔案内容,例如
root:x:0:0:root:/root:/bin/bash
4:1:1:1:4:5:9
vim example_1.awk
BEGIN{
FS=":"
}
{
i=1
while(i<=NF)
{
if(i==NF)
printf "%d",length($i)
else
printf "%d:",length($i)
i++
}
print ""
}
#腳本檔案執行指令
awk -f example_1.awk /etc/passwd
- 搜尋字元串"I have a dream"中出現"ea"子串的位置;
(1)、awk 'BEGIN{str="I have a dream";location=index(str,"ea");print location}'
(2)、awk 'BEGIN{str="I have a dream";location=match(str,"ea");print location}'
- 将字元串"Hadoop is a bigdata Framework"全部轉換為小寫;
- 将字元串"Hadoop is a Bigdata Framework"全部轉換為大寫;
- 将字元串"Hadoop kafka Spark Storm HDFS YARN Zookeeper",按空格為分隔符,分隔每部分儲存到數組array中;
- 搜尋字元串"Tranction 2345 Start:Select * from master"第一個數字出現的位置;
- 截取字元串"transaction start"的子串,截取條件從第4個字元開始,截取5位;
awk 'BEGIN{str="transaction start";print substr(str,4,5)}'
#從第4個字元開始,截取剩下所有的字元
awk 'BEGIN{str="transaction start";print substr(str,4)}'
- 替換字元串"Tranction 243 Start,Event ID:9002"中第一個比對到的數字串為$符号;
awk 'BEGIN{str="Tranction 243 Start,Event ID:9002";count=sub(/[0-9]+/,"$",str);print count,str}'
#替換所有的
awk 'BEGIN{str="Tranction 243 Start,Event ID:9002";count=gsub(/[0-9]+/,"$",str);print count,str}'
8. awk中的常用選項
8.1. awk選項總結
選項 | 解釋 |
---|---|
-v | 參數傳遞 |
-f | 指定腳本檔案 |
-F | 指定分隔符 |
-V | 檢視awk的版本号 |
num1=20
var="hello world"
awk -v num2=$num1 -v var1="$var" 'BEGIN{print num2,var1}'
#輸出結果
20 hello world
awk -v num2="$num1" -v var1="$var" 'BEGIN{print num2,var1}'
#輸出結果
20 hello world
vim test.awk
BEGIN{print num2,var1}
awk -v num2="$num1" -v var1="$var" -f test.awk
9. awk中數組的用法
9.1. Shell中數組的用法
array=("Allen" "Mike" "Messi" "Jerry" "Hanmeimei" "Wang")
#列印元素:
echo ${array[2]}
#列印元素個數:
echo ${#array[@]}
#列印元素長度:
echo ${#array[3]}
#給元素指派:
array[3]="Li"
#删除元素:
unset array[2];unset array
#分片通路:
echo ${array[@]:1:3}
#元素内容替換:
${array[@]/e/E} #隻替換單個字段第一個e
${array[@]//e/E} #替換所有的e
#數組的周遊
for a in ${array[@]}
do
echo $a
done
9.2. awk中數組的用法
在awk中,使用數組時,不僅可以使用1.2…n作為數組下标,也可以使用字元串作為數組下标。
- 當使用1.2.3…n時,直接使用array[2]通路元素;需要周遊數組時,使用以下形式:
str="Allen Jerry Mike Tracy Jordan Kobe Garnet"
split()
for(i=1;i<length(array);i++)
print array[i]
- 當使用字元串作為數組下标時,需要使用array[str]形式通路元素;周遊數組時,使用以下形式:
array["var1"]="Jin"
array["var2"]="Hao"
array["var3"]=Fang
典型常用例子:
- 統計主機上所有的TCP連接配接狀态數,按照每個TCP狀态分類
- 計算橫向資料總和,計算縱向資料總和;
# student.txt
allen 80 90 87 91
mike 78 86 93 96
Kobe 66 92 82 78
Jerry 98 74 66 54
Wang 87 21 100 43
awk 'BEGIN{printf "%-10s%-10s%-10s%-10s%-10s\n","Name","Chinese","Math","English","Physical"}{total=$2+$3+$4+$5;printf "%-10s%-10d%-10d%-10d%-10d%-10d%-10d\n",$1,$2,$3,$4,$5,total}' student.txt
#或者
vim student.awk
BEGIN{
printf "%-10s%-10s%-10s%-10s%-10s\n","Name","Chinese","Math","English","Physical","Total"
}
{
total=$2+$3+$4+$5
chinese_sum+=$2
math_sum+=$3
english_sum+=$4
physical_sum+=$5
printf "%-10s%-10d%-10d%-10d%-10d%-10d%-10d\n",$1,$2,$3,$4,$5,total
}
END {
printf "%-10s%-10d%-10d%-10d%-10d\n","",chinese_sum,math_sum,english_sum,physical_sum
}
10. 一個複雜的awk處理生産資料的例子
需求描述:利用awk處理日志,并生成結果報告。
生成資料腳本insert.sh,内容如下:
#!/bin/bash
#
function create_random()
{
min=$1
max=$(($2-$min+1))
num=$(date +%s%N)
echo $(($num%$max+$min))
}
INDEX=1
while true
do
for user in Allen Mike Jerry Tracy Hanmeimei Lilei
do
COUNT=$RANDOM
NUM1=`create_random 1 $COUNT`
NUM2=`expr $COUNT - $NUM`
echo "`date '+%Y-%m-%d %H:%M:%S'` $INDEX Batches: user $user INSERT $COUNT records into database:product table:detail, Insert $NUM1 records successfully, failed $NUM2 record" >> ./db.log.`date +%Y%m%d`
INDEX=`expr $INDEX + 1`
done
done
資料格式如下:
2019-01-29 00:58:30 1 Batches:user allen insert 22498 records into database:product table detail,insert 20771 records successfully,failed 1727 records
2019-01-29 00:58:30 2 Batches:user mike insert 29378 records into database:product table detail,insert 21426 records successfully,failed 7952 records
2019-01-29 00:58:30 3 Batches:user jerry insert 22779 records into database:product table detail, insert 9397 records successfully, failed 13382 records
2019-01-29 00:58:30 4 Batches:user tracy insert 25232 records into database:product table detail, insert 21255 records successfully, failed 3977 records
- 統計每個人員分别插入了多少條record 進資料庫;
#輸出結果:
USER Total_Records
allen 493082
mike 349287
vim exam_1.awk
BEGIN{
printf "%-10s%-10s\n","User","Total Records"
}
{
USER[$6]+=$8
}
END
{
for(u in USER)
printf "%-10s%-20d\n" u,USER[u]
}
#腳本檔案執行指令
awk -f exam_1.awk db.log.20190129
- 統計每個人分别插入成功了多少record,失敗了多少record;
#輸出結果:
User Success_Record Failed_Records
jerry 3472738 283737
mike 2738237 28373
BEGIN {
printf "%-10s%-20s%-20s\n","User","Success_Records","Failed_Records"
}
{
SUCCESS[$6]+=$14
FAIL[$6]+=$17
}
END {
for(u in SUCCESS)
printf "%-10s%-20d%-20d%-20d\n",u,SUCCESS[u],FAIL[u]
}
- 将例子1和例子2結合起來,一起輸出,輸出每個人分别插入多少資料,多少成功,多少失敗,并且格式化輸出,加上标題;
vim exam_3.awk
BEGIN {
printf "%-10s%-20s%-20s%-20s\n","User","Total","Success","Failed"
}
{
TOTAL[$6]+=$8
SUCCESS[$6]+=$14
FAIL[$6]+=$17
}
END {
for(u in SUCCESS)
printf "%-10s%-20d%-20d%-20d%-20d\n",u,TOTAL[u],SUCCESS[u],FAIL[u]
}
#腳本檔案執行指令
awk -f exam_3.awk db.log.20190129
- 在例子3的基礎上,加上結尾,統計全部插入記錄數,成功記錄數,失敗記錄數;
vim exam_4.awk
BEGIN {
printf "%-10s%-20s%-20s%-20s\n","User","Total","Success","Failed"
}
{
TOTAL[$6]+=$8
SUCCESS[$6]+=$14
FAIL[$6]+=$17
}
END {
for(u in SUCCESS)
{
total+=TOTAL[u]
success+=SUCCESS[u]
fail+=FAIL[u]
printf "%-10s%-20d%-20d%-20d%-20d\n",u,TOTAL[u],SUCCESS[u],FAIL[u]
}
printf "%-10s%-20d%-20d%-20d%-20d\n","",total,success,fail
}
- 查找丢失資料的現象,也就是成功+失敗的記錄數,不等于一共插入的記錄數。找出這些數字并顯示行号和對應行的日志資訊;
awk '{if($8!=$14+$17) print NR,$0}' db.log.20190129