shell編程(八):文本處理三劍客之awk

awk的工作模式

語法格式

第一種形式

awk 'BEGIN{}pattern{commands}END{}' file_name
  • BEGIN{}表示文本開始之前,進行{}內的操作
  • END{}表示文本開始以後,進行{}內的操作
  • pattern 表示對那些行進行操作,不寫則默認是所有行
  • {commands}對行進行的操作,可以寫多個命令
    第二種形式
cat file_name | awk 'BEGIN{}pattern{commands}END{}'

awk的內置變量

n內置變量 含義
$0 整行內容
11-n 當前行的第1-n個字段
NF 當前行的字段個數,即多少列
NR 當前行的行號,從1開始計數
FNR 多個文件處理時,每個文件行號單獨計數,都是從0開始
FS 輸入字段分隔符。不指定默認以空格或tab分割
RS 輸入行分割符,默認回車換行
OFS 輸出字段分隔符,默認空格
ORS 輸出行分隔符。默認回車
FILENAME 當前輸入的文件名
ARGC 命令行參數個數
ARGV 命令行參數數組

示例

  • list內容
java:python:scala
hadoop:spark:flume
jake:mike:coco
awk 'BEGIN{FS=":"}{print $1}' list # BEGIN中指定分隔符爲:
awk 'BEGIN{FS=":"}{print NR}' list # 顯示行號
awk 'BEGIN{FS=":"}{print NF}' list # 顯示每行的列數
awk 'BEGIN{FS=":"}{print NF}' list list #行號爲連續顯示
awk 'BEGIN{FS=":"}{print FNR}' list list1 # 每個文件是單獨計算行號
awk 'BEGIN{FS=":"}{print $NF}' list # 輸出最後一列

格式化輸出printf

格式說明符

格式符 含義
%s 打印字符串
%d 打印十進制數

修飾符

修飾符 含義
- 左對齊
+ 右對齊
+ 顯示8進制在前面加0,顯示16進制在前面加0x

示例

以字符串格式化打印 /etc/passwd中的第七個字段,並且以":" 作爲分隔符

awk 'BEGIN{FS=":"} {printf "%s:",$7}' /etc/passwd

以10進制打印/etc/passwd中第三個字段,以":"作爲分隔

awk 'BEGIN{FS=":"} {printf "%10d\n",$3}' /etc/passwd # 右對齊10個字符

浮點數打印

awk 'BEGIN{FS=":"} {printf "%0.2f\n",$3}' /etc/passwd # 浮點數打印 保留兩位小數

awk模式匹配的兩種方法

第一種模式匹配:RegExp正則表達式匹配

示例
匹配/etc/passwd文件中含有root字符串的所有行

awk 'BEGIN{FS=":"}/root/{print $0}' /etc/passwd

匹配/etc/passwd文件中以yarn開頭的所有行

awk 'BEGIN{FS=":"}/^yarn/{print $0}' /etc/passwd

第二種模式匹配:關係運算匹配

  • 支持 常用關係運算符 > < >= <= == !=
  • 還支持 ~:匹配正則表達式
  • !~不匹配正則表達式

示例
匹配/etc/passwd中第三個字段小於50的行

awk 'BEGIN{FS=":"}$3<50{print $0}' /etc/passwd

匹配第三個字段包含3個或3個以上的數字信息的所有行

awk 'BEGIN{FS=":"}$3~/[0-9]{3,}/{print $0}' /etc/passwd  # /[0-9]{3,}/ 固定寫法

布爾運算符匹配
|| 或
&& 與
! 非

匹配/etc/passwd文件中包含hdfs或yarn的所有行信息

awk 'BEGIN{FS=":"}$1=="hdfs" || $1=="yarn" {print $0}' /etc/passwd

awk中表達式的用法

符號 含義
+
* 乘法
-
/ 除法
% 取模
^或** 乘方
++x 先加1
x++ 後加1

統計/etc/services中空白行的數量

awk '/^$/{sum++} END{print sum}' /etc/services # /^$/ 表示空白行

統計學生分數平均值
學生課程文件如下:

Allen 80 90 96 98
mike 93 98 92 91
zhang 78 76 87 92
awk 'BEGIN{printf "%-8s%-8s%-8s%-8s%-8s%-8s\n","name","math","english","chinese","history","avg"} {total=$2+$3+$4+$5;avg=total/4;printf "%-8s%-8d%-8d%-8d%-8d%-0.2f\n",$1,$2,$3,$4,$5,avg}' stu.txt

awk動作中的條件及循環語句

條件語句

if(條件表達式)
	動作1
else if
	動作2
else
	動作3

示例
只打印/etc/passwd/中第3個字段的數值在50-100範圍內的行

awk 'BEGIN{FS=":"}{if($3<100 && $3>50) print($0)}' /etc/passwd

將命令寫到文件中,並且執行 main.awk

BEGIN{FS=":"}{if($4<100 && $3>50) print($0)}

執行如下語句

awk -f main.awk /etc/passwd

循環語句

while循環
do while
for循環,語法格式和c語言一致。
示例
計算1+2+3+…100的和,使用while、do while、for循環三種方式實現

# for循環
BEGIN{
        for(i=0;i<=100;i++)
        {
                sum+=i
        }
        print sum
}
# while 
BEGIN{
        while(i<=100){
                sum+=i
                i++
        }
        print sum
}
#do while
BEGIN{
        i=0
        do{
           sum+=i
           i++
        }while(i<=100)
        print sum
}

awk中的字符串函數

常用字符串函數

函數名 解釋 函數返回值
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中每行,每個字段的長度

BEGIN{
    FS=":"
}
{   i=0
    while(i<=NF)
    {
        if(i==NF){
            printf "%d",$i
        }
        else{
            printf "%d:",$i
        }
        i++
    }
    print ""
}

搜索字符串"I have s dream" 中出現"ea"子串的位置

# 使用index
awk 'BEGIN{str="I have s dream";location=index(str,"ea");print location}'

# 使用match
awk 'BEGIN{str="I have s dream";location=match(str,"ea");print location}' 

字符串大小寫轉換

awk 'BEGIN{str="I have s dream";print tolower(str)}' 
awk 'BEGIN{str="I have s dream";print toupper(str)}'

字符串分割

awk 'BEGIN{str="I have s dream";split(str,arr," ");for(i in arr) print arr[i]}'

截取子串

awk 'BEGIN{str="I have s dream"; print substr(str,4,5)}'
awk 'BEGIN{str="I have s dream"; print substr(str,4)}' # 第四位開始 

字符串替換

# 將123替換爲$ 符號。sub返回的個數,直接修改str
awk 'BEGIN{str="my 123 dream"; sub(/[0-9]+/,"$",str);print str}'

awk選項總結

選項 解釋
-v 參數傳遞
-f 指定腳本文件
-F 指定分隔符
-V 查看awk版本號

傳遞參數

num1=20
var="hello world"
awk -v num2="$num1" -v var1="$var" 'BEGIN{print num2,var1}'

awk中的數組

shell中數組用法

定義

array=("Allen" "Mike" "Jick") # 空格分割

數組操作

# 打印元素
echo ${array[2]}
# 打印數組元素個數
echo ${#array[@]}
# 打印數組元素長度
echo ${#array[3]}
# 給元素複製
array[3]="Li"
# 刪除元素
unset array[0] # 被刪掉後 下標不變
# 分片訪問
echo ${array[@]:1:3}
# 元素內容替換
${array[@]/e/E} # 只替換第一個e
${array[@]//e/E} # 替換所有的e

數組遍歷

for a in ${array[@]}
do
    echo $a
done

awk中的數組

在awk中,使用數組時,不僅可以使用1,2,3…作爲數組下標,還可以使用字符串作爲下標
使用數字作爲下標

str="Allen Jerry Mike"
split(str,array)
for(i=1;i<=length(array);i++)
	print array[i]

使用字符串作爲下標

array["var1"]="Jin"
array["var2"]="Hao"
array["var3"]="Fang"
for(a in array)
	print array[a]

awk數組示例

統計tcp鏈接狀態個數

netstat -an | grep tcp | awk '{a[$6]++}END{for(i in a) print i,a[i]}'

計算橫向數據總合,計算縱向數據總和

  • 數據
    allen 80 90 87 91
    mike 99 100 100 80
    kobe 99 98 99 100
  • 腳本
BEGIN{
    printf "%-10s%-10s%-10s%-10s%-10s\n","name","yuwen","math","english","total"
}
{
    total=$2+$3+$4
    yuwen_sum+=$2 # 列之間的數累加
    math_sum+=$3
    enlish_sum+=$4
    printf "%-10s%-10s%-10s%-10s%-10s\n",$1,$2,$3,$4,total
}
END{
    printf "%-10s%-10s%-10s%-10s%-10s\n","",yuwen_sum,math_sum,enlish_sum,""
}

awk中getline用法

getline可以實現同時讀取兩個文件進行操作
示例

  • file1
A 1
B 2
C 3
  • file2
A
C
  • 腳本
BEGIN{
    while(getline<"file1"){a[$1]=$2}  # 將file1中加載到數組中
}
{
    if($1 in a) print $0  # 如果file2中的$1在數組a中 則打印
}
END{
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章