shell編程(七)

1、awk介紹
1.awk概述
2.awk能幹啥?
2、awk使用方式
1.==命令行模式使用==
1)語法結構
2)經常使用選項介紹
3)=='==命名部分說明=='==
2.腳本模式使用
1)腳本編寫
2)腳本執行
3、 awk內部相關變量
一、==經常使用內置變量舉例==
二、內置變量分隔符舉例
4、 awk工做原理
5、awk使用進階
1.格式化輸出print和printf
2.awk變量定義
3.awk中BEGIN...END使用
1)舉例說明1
2)舉例說明2
4.awk和正則的綜合運用
1)舉例說明
5.課堂練習
6.awk的腳本編程
1)流程控制語句
2)循環語句
7.awk算數運算
6、awk統計案例
一、統計系統中各類類型的shell
二、統計網站訪問狀態
三、統計訪問網站的每一個IP的數量
四、統計網站日誌中PV量php

1、awk介紹

1. awk概述

  • awk是一種==編程語言==,主要用於在linux/unix下對==文本和數據==進行處理,是linux/unix下的一個工具。數據能夠來自標準輸入、一個或多個文件,或其它命令的輸出。
  • awk的處理文本和數據的方式:==逐行掃描==文件,默認從第一行到最後一行,尋找匹配的==特定模式==的行,並在這些行上進行你想要的操做。
  • awk分別表明其做者姓氏的第一個字母。由於它的做者是三我的,分別是Alfred Aho、Brian Kernighan、Peter Weinberger。
  • gawk是awk的GNU版本,它提供了Bell實驗室和GNU的一些擴展。mysql

  • 下面介紹的awk是以GNU的gawk爲例的,在linux系統中已把awk連接到gawk,因此下面所有以awk進行介紹。

2. awk能幹啥?

  1. awk==用來處理文件和數據==的,是類unix下的一個工具,也是一種編程語言
  2. 能夠用來==統計數據==,好比網站的訪問量,訪問的IP量等等
  3. 支持條件判斷,支持for和while循環

2、awk使用方式

1. ==命令行模式使用==

1)語法結構

awk 選項 '命令部分' 文件名

特別說明:
引用shell變量需用雙引號引發

2)經常使用選項介紹

  • ==-F== 定義字段分割符號,默認的分隔符是==空格==
  • -v 定義變量並賦值

3)=='==命名部分說明=='==

  • 正則表達式,地址定位
'/root/{awk語句}'             sed中: '/root/p'
'NR==1,NR==5{awk語句}'            sed中: '1,5p'
'/^root/,/^ftp/{awk語句}'     sed中:'/^root/,/^ftp/p'
  • {awk語句1==;==awk語句2==;==...}
'{print $0;print $1}'       sed中:'p'
'NR==5{print $0}'               sed中:'5p'
注:awk命令語句間用分號間隔
  • BEGIN...END....
'BEGIN{awk語句};{處理中};END{awk語句}'
'BEGIN{awk語句};{處理中}'
'{處理中};END{awk語句}'

2. 腳本模式使用

1)腳本編寫

#!/bin/awk -f       定義魔法字符
如下是awk引號裏的命令清單,不要用引號保護命令,多個命令用分號間隔
BEGIN{FS=":"}
NR==1,NR==3{print $1"\t"$NF}
...

2)腳本執行

方法1:
awk 選項 -f awk的腳本文件  要處理的文本文件
awk -f awk.sh filename

sed -f sed.sh -i filename

方法2:
./awk的腳本文件(或者絕對路徑)  要處理的文本文件
./awk.sh filename

./sed.sh filename

3、 awk內部相關變量

變量 變量說明 備註
==$0== 當前處理行的全部記錄
==\$1,\$2,\$3...\$n== 文件中每行以==間隔符號==分割的不一樣字段 awk -F: '{print \$1,\$3}'
==NF== 當前記錄的字段數(列數) awk -F: '{print NF}'
==$NF== 最後一列 $(NF-1)表示倒數第二列
==FNR/NR== 行號
==FS== 定義間隔符 'BEGIN{FS=":"};{print \$1,$3}'
==OFS== 定義輸出字段分隔符,==默認空格== 'BEGIN{OFS="\t"};print \$1,$3}'
RS 輸入記錄分割符,默認換行 'BEGIN{RS="\t"};{print $0}'
ORS 輸出記錄分割符,默認換行 'BEGIN{ORS="\n\n"};{print \$1,$3}'
FILENAME 當前輸入的文件名

一、==經常使用內置變量舉例==

# awk -F: '{print $1,$(NF-1)}' 1.txt
# awk -F: '{print $1,$(NF-1),$NF,NF}' 1.txt
# awk '/root/{print $0}' 1.txt
# awk '/root/' 1.txt
# awk -F: '/root/{print $1,$NF}' 1.txt 
root /bin/bash
# awk -F: '/root/{print $0}' 1.txt      
root:x:0:0:root:/root:/bin/bash
# awk 'NR==1,NR==5' 1.txt 
# awk 'NR==1,NR==5{print $0}' 1.txt
# awk 'NR==1,NR==5;/^root/{print $0}' 1.txt 
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

二、內置變量分隔符舉例

FS和OFS:
# awk 'BEGIN{FS=":"};/^root/,/^lp/{print $1,$NF}' 1.txt
# awk -F: 'BEGIN{OFS="\t\t"};/^root/,/^lp/{print $1,$NF}' 1.txt        
root            /bin/bash
bin             /sbin/nologin
daemon          /sbin/nologin
adm             /sbin/nologin
lp              /sbin/nologin
# awk -F: 'BEGIN{OFS="@@@"};/^root/,/^lp/{print $1,$NF}' 1.txt     
root@@@/bin/bash
bin@@@/sbin/nologin
daemon@@@/sbin/nologin
adm@@@/sbin/nologin
lp@@@/sbin/nologin
[root@server shell07]# 

RS和ORS:
修改源文件前2行增長製表符和內容:
vim 1.txt
root:x:0:0:root:/root:/bin/bash hello   world
bin:x:1:1:bin:/bin:/sbin/nologin        test1   test2

# awk 'BEGIN{RS="\t"};{print $0}' 1.txt
# awk 'BEGIN{ORS="\t"};{print $0}' 1.txt

4、 awk工做原理

awk -F: '{print $1,$3}' /etc/passwdlinux

  1. awk使用一行做爲輸入,並將這一行賦給內部變量$0,每一行也可稱爲一個記錄,以換行符(RS)結束正則表達式

  2. 每行被間隔符==:==(默認爲空格或製表符)分解成字段(或域),每一個字段存儲在已編號的變量中,從$1開始sql

    問:awk如何知道用空格來分隔字段的呢?shell

    答:由於有一個內部變量==FS==來肯定字段分隔符。初始時,FS賦爲空格編程

  3. awk使用print函數打印字段,打印出來的字段會以==空格分隔==,由於\$1,\$3之間有一個逗號。逗號比較特殊,它映射爲另外一個內部變量,稱爲==輸出字段分隔符==OFS,OFS默認爲空格vim

  4. awk處理完一行後,將從文件中獲取另外一行,並將其存儲在$0中,覆蓋原來的內容,而後將新的字符串分隔成字段並進行處理。該過程將持續到全部行處理完畢

5、awk使用進階

1. 格式化輸出printprintf

print函數     相似echo "hello world"
# date |awk '{print "Month: "$2 "\nYear: "$NF}'
# awk -F: '{print "username is: " $1 "\t uid is: "$3}' /etc/passwd

printf函數        相似echo -n
# awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}'  /etc/passwd
# awk -F: '{printf "|%15s| %10s| %15s|\n", $1,$2,$3}' /etc/passwd
# awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd

awk 'BEGIN{FS=":"};{printf "%-15s %-15s %-15s\n",$1,$6,$NF}' a.txt

%s 字符類型  strings            %-20s
%d 數值類型 
佔15字符
- 表示左對齊,默認是右對齊
printf默認不會在行尾自動換行,加\n

2. awk變量定義

# awk -v NUM=3 -F: '{ print $NUM }' /etc/passwd
# awk -v NUM=3 -F: '{ print NUM }' /etc/passwd
# awk -v num=1 'BEGIN{print num}' 
1
# awk -v num=1 'BEGIN{print $num}' 
注意:
awk中調用定義的變量不須要加$

3. awk中BEGIN...END使用

​ ①==BEGIN==:表示在==程序開始前==執行瀏覽器

​ ②==END== :表示全部文件==處理完後==執行bash

​ ③用法:'BEGIN{開始處理以前};{處理中};END{處理結束後}'

1)舉例說明1

打印最後一列和倒數第二列(登陸shell和家目錄)

awk -F: 'BEGIN{ print "Login_shell\t\tLogin_home\n*******************"};{print $NF"\t\t"$(NF-1)};END{print "************************"}' 1.txt

awk 'BEGIN{ FS=":";print "Login_shell\tLogin_home\n*******************"};{print $NF"\t"$(NF-1)};END{print "************************"}' 1.txt

Login_shell     Login_home
************************
/bin/bash       /root
/sbin/nologin       /bin
/sbin/nologin       /sbin
/sbin/nologin       /var/adm
/sbin/nologin       /var/spool/lpd
/bin/bash       /home/redhat
/bin/bash       /home/user01
/sbin/nologin       /var/named
/bin/bash       /home/u01
/bin/bash       /home/YUNWEI
************************************

2)舉例說明2

打印/etc/passwd裏的用戶名、家目錄及登陸shell

u_name      h_dir       shell
***************************

***************************

awk -F: 'BEGIN{OFS="\t\t";print"u_name\t\th_dir\t\tshell\n***************************"};{printf "%-20s %-20s %-20s\n",$1,$(NF-1),$NF};END{print "****************************"}'

# awk -F: 'BEGIN{print "u_name\t\th_dir\t\tshell" RS "*****************"}  {printf "%-15s %-20s %-20s\n",$1,$(NF-1),$NF}END{print "***************************"}'  /etc/passwd

格式化輸出:
echo        print
echo -n printf

{printf "%-15s %-20s %-20s\n",$1,$(NF-1),$NF}

4. awk和正則的綜合運用

運算符 說明
== 等於
!= 不等於
> 大於
< 小於
>= 大於等於
<= 小於等於
~ 匹配
!~ 不匹配
! 邏輯非
&& 邏輯與
|| 邏輯或

1)舉例說明

從第一行開始匹配到以lp開頭行
awk -F: 'NR==1,/^lp/{print $0 }' passwd  
從第一行到第5行          
awk -F: 'NR==1,NR==5{print $0 }' passwd
從以lp開頭的行匹配到第10行       
awk -F: '/^lp/,NR==10{print $0 }' passwd 
從以root開頭的行匹配到以lp開頭的行       
awk -F: '/^root/,/^lp/{print $0}' passwd
打印以root開頭或者以lp開頭的行            
awk -F: '/^root/ || /^lp/{print $0}' passwd
awk -F: '/^root/;/^lp/{print $0}' passwd
顯示5-10行   
awk -F':' 'NR>=5 && NR<=10 {print $0}' /etc/passwd     
awk -F: 'NR<10 && NR>5 {print $0}' passwd 

打印30-39行以bash結尾的內容:
[root@MissHou shell06]# awk 'NR>=30 && NR<=39 && $0 ~ /bash$/{print $0}' passwd 
stu1:x:500:500::/home/stu1:/bin/bash
yunwei:x:501:501::/home/yunwei:/bin/bash
user01:x:502:502::/home/user01:/bin/bash
user02:x:503:503::/home/user02:/bin/bash
user03:x:504:504::/home/user03:/bin/bash

[root@MissHou shell06]# awk 'NR>=3 && NR<=8 && /bash$/' 1.txt  
stu7:x:1007:1007::/rhome/stu7:/bin/bash
stu8:x:1008:1008::/rhome/stu8:/bin/bash
stu9:x:1009:1009::/rhome/stu9:/bin/bash

打印文件中1-5而且以root開頭的行
[root@MissHou shell06]# awk 'NR>=1 && NR<=5 && $0 ~ /^root/{print $0}' 1.txt
root:x:0:0:root:/root:/bin/bash
[root@MissHou shell06]# awk 'NR>=1 && NR<=5 && $0 !~ /^root/{print $0}' 1.txt
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

理解;號和||的含義:
[root@MissHou shell06]# awk 'NR>=3 && NR<=8 || /bash$/' 1.txt
[root@MissHou shell06]# awk 'NR>=3 && NR<=8;/bash$/' 1.txt

打印IP地址
# ifconfig eth0|awk 'NR>1 {print $2}'|awk -F':' 'NR<2 {print $2}'    
# ifconfig eth0|grep Bcast|awk -F':' '{print $2}'|awk '{print $1}'
# ifconfig eth0|grep Bcast|awk '{print $2}'|awk -F: '{print $2}'

# ifconfig eth0|awk NR==2|awk -F '[ :]+' '{print $4RS$6RS$8}'
# ifconfig eth0|awk -F"[ :]+" '/inet addr:/{print $4}'

5. 課堂練習

  1. 顯示能夠登陸操做系統的用戶全部信息 從第7列匹配以bash結尾,輸出整行(當前行全部的列)
[root@MissHou ~] awk '/bash$/{print $0}'    /etc/passwd
[root@MissHou ~] awk '/bash$/{print $0}' /etc/passwd
[root@MissHou ~] awk '/bash$/' /etc/passwd
[root@MissHou ~] awk -F: '$7 ~ /bash/' /etc/passwd
[root@MissHou ~] awk -F: '$NF ~ /bash/' /etc/passwd
[root@MissHou ~] awk -F: '$0 ~ /bash/' /etc/passwd
[root@MissHou ~] awk -F: '$0 ~ /\/bin\/bash/' /etc/passwd
  1. 顯示能夠登陸系統的用戶名
# awk -F: '$0 ~ /\/bin\/bash/{print $1}' /etc/passwd
  1. 打印出系統中普通用戶的UID和用戶名
500 stu1
501 yunwei
502 user01
503 user02
504 user03

# awk -F: 'BEGIN{print "UID\tUSERNAME"} {if($3>=500 && $3 !=65534 ) {print $3"\t"$1} }' /etc/passwdUID  USERNAME

# awk -F: '{if($3 >= 500 && $3 != 65534) print $1,$3}' a.txt 
redhat 508
user01 509
u01 510
YUNWEI 511

6. awk的腳本編程

1)流程控制語句

① if結構

if語句:

if [ xxx ];then
xxx
fi

格式:
awk 選項 '正則,地址定位{awk語句}'  文件名

{ if(表達式){語句1;語句2;...}}

awk -F: '{if($3>=500 && $3<=60000) {print $1,$3} }' passwd

# awk -F: '{if($3==0) {print $1"是管理員"} }' passwd 
root是管理員

# awk 'BEGIN{if('$(id -u)'==0) {print "admin"} }'
admin

② if...else結構

if...else語句:
if [ xxx ];then
    xxxxx

else
    xxx
fi

格式:
{if(表達式){語句;語句;...}else{語句;語句;...}}

awk -F: '{ if($3>=500 && $3 != 65534) {print $1"是普通用戶"} else {print $1,"不是普通用戶"}}' passwd 

awk 'BEGIN{if( '$(id -u)'>=500 && '$(id -u)' !=65534 ) {print "是普通用戶"} else {print "不是普通用戶"}}'

③ if...elif...else結構

if [xxxx];then
    xxxx
elif [xxx];then
    xxx
....
else
...
fi

if...else if...else語句:

格式:
{ if(表達式1){語句;語句;...}else if(表達式2){語句;語句;...}else if(表達式3){語句;語句;...}else{語句;語句;...}}

awk -F: '{ if($3==0) {print $1,":是管理員"} else if($3>=1 && $3<=499 || $3==65534 ) {print $1,":是系統用戶"} else {print $1,":是普通用戶"}}'

awk -F: '{ if($3==0) {i++} else if($3>=1 && $3<=499 || $3==65534 ) {j++} else {k++}};END{print "管理員個數爲:"i "\n系統用戶個數爲:"j"\n普通用戶的個數爲:"k }'

# awk -F: '{if($3==0) {print $1,"is admin"} else if($3>=1 && $3<=499 || $3==65534) {print $1,"is sys users"} else {print $1,"is general user"} }' a.txt 

root is admin
bin is sys users
daemon is sys users
adm is sys users
lp is sys users
redhat is general user
user01 is general user
named is sys users
u01 is general user
YUNWEI is general user

awk -F: '{  if($3==0) {print $1":管理員"} else if($3>=1 && $3<500 || $3==65534 ) {print $1":是系統用戶"} else {print $1":是普通用戶"}}'   /etc/passwd

awk -F: '{if($3==0) {i++} else if($3>=1 && $3<500 || $3==65534){j++} else {k++}};END{print "管理員個數爲:" i RS "系統用戶個數爲:"j RS "普通用戶的個數爲:"k }' /etc/passwd
管理員個數爲:1
系統用戶個數爲:28
普通用戶的個數爲:27

# awk -F: '{ if($3==0) {print $1":是管理員"} else if($3>=500 && $3!=65534) {print $1":是普通用戶"} else {print $1":是系統用戶"}}' passwd 

awk -F: '{if($3==0){i++} else if($3>=500){k++} else{j++}} END{print i; print k; print j}' /etc/passwd

awk -F: '{if($3==0){i++} else if($3>999){k++} else{j++}} END{print "管理員個數: "i; print "普通用個數: "k; print "系統用戶: "j}' /etc/passwd 

若是是普通用戶打印默認shell,若是是系統用戶打印用戶名
# awk -F: '{if($3>=1 && $3<500 || $3 == 65534) {print $1} else if($3>=500 && $3<=60000 ) {print $NF} }' /etc/passwd

2)循環語句

① for循環

打印1~5
for ((i=1;i<=5;i++));do echo $i;done

# awk 'BEGIN { for(i=1;i<=5;i++) {print i} }'
打印1~10中的奇數
# for ((i=1;i<=10;i+=2));do echo $i;done|awk '{sum+=$0};END{print sum}'
# awk 'BEGIN{ for(i=1;i<=10;i+=2) {print i} }'
# awk 'BEGIN{ for(i=1;i<=10;i+=2) print i }'

計算1-5的和
# awk 'BEGIN{sum=0;for(i=1;i<=5;i++) sum+=i;print sum}'
# awk 'BEGIN{for(i=1;i<=5;i++) (sum+=i);{print sum}}'
# awk 'BEGIN{for(i=1;i<=5;i++) (sum+=i);print sum}'

② while循環

打印1-5
# i=1;while (($i<=5));do echo $i;let i++;done

# awk 'BEGIN { i=1;while(i<=5) {print i;i++} }'
打印1~10中的奇數
# awk 'BEGIN{i=1;while(i<=10) {print i;i+=2} }'
計算1-5的和
# awk 'BEGIN{i=1;sum=0;while(i<=5) {sum+=i;i++}; print sum }'
# awk 'BEGIN {i=1;while(i<=5) {(sum+=i) i++};print sum }'

③ 嵌套循環

嵌套循環:
#!/bin/bash
for ((y=1;y<=5;y++))
do
    for ((x=1;x<=$y;x++))
    do
        echo -n $x  
    done
echo
done

awk 'BEGIN{ for(y=1;y<=5;y++) {for(x=1;x<=y;x++) {printf x} ;print } }'

# awk 'BEGIN { for(y=1;y<=5;y++) { for(x=1;x<=y;x++) {printf x};print} }'
1
12
123
1234
12345

# awk 'BEGIN{ y=1;while(y<=5) { for(x=1;x<=y;x++) {printf x};y++;print}}'
1
12
123
1234
12345

嘗試用三種方法打印99口訣表:
#awk 'BEGIN{for(y=1;y<=9;y++) { for(x=1;x<=y;x++) {printf x"*"y"="x*y"\t"};print} }'

#awk 'BEGIN{for(y=1;y<=9;y++) { for(x=1;x<=y;x++) printf x"*"y"="x*y"\t";print} }'
#awk 'BEGIN{i=1;while(i<=9){for(j=1;j<=i;j++) {printf j"*"i"="j*i"\t"};print;i++ }}'

#awk 'BEGIN{for(i=1;i<=9;i++){j=1;while(j<=i) {printf j"*"i"="i*j"\t";j++};print}}'

循環的控制:
break       條件知足的時候中斷循環
continue    條件知足的時候跳過循環
# awk 'BEGIN{for(i=1;i<=5;i++) {if(i==3) break;print i} }'
1
2
# awk 'BEGIN{for(i=1;i<=5;i++){if(i==3) continue;print i}}'
1
2
4
5

7. awk算數運算

+ - * / %(模) ^(冪2^3)
能夠在模式中執行計算,awk都將按浮點數方式執行算術運算
# awk 'BEGIN{print 1+1}'
# awk 'BEGIN{print 1**1}'
# awk 'BEGIN{print 2**3}'
# awk 'BEGIN{print 2/3}'

6、awk統計案例

一、統計系統中各類類型的shell

# awk -F: '{ shells[$NF]++ };END{for (i in shells) {print i,shells[i]} }' /etc/passwd

books[linux]++
books[linux]=1
shells[/bin/bash]++
shells[/sbin/nologin]++

/bin/bash 5
/sbin/nologin 6

shells[/bin/bash]++         a
shells[/sbin/nologin]++     b
shells[/sbin/shutdown]++    c

books[linux]++
books[php]++

二、統計網站訪問狀態

# ss -antp|grep 80|awk '{states[$1]++};END{for(i in states){print i,states[i]}}'
TIME_WAIT 578
ESTABLISHED 1
LISTEN 1

# ss -an |grep :80 |awk '{states[$2]++};END{for(i in states){print i,states[i]}}'
LISTEN 1
ESTAB 5
TIME-WAIT 25

# ss -an |grep :80 |awk '{states[$2]++};END{for(i in states){print i,states[i]}}' |sort -k2 -rn
TIME-WAIT 18
ESTAB 8
LISTEN 1

三、統計訪問網站的每一個IP的數量

# netstat -ant |grep :80 |awk -F: '{ip_count[$8]++};END{for(i in ip_count){print i,ip_count[i]} }' |sort

# ss -an |grep :80 |awk -F":" '!/LISTEN/{ip_count[$(NF-1)]++};END{for(i in ip_count){print i,ip_count[i]}}' |sort -k2 -rn |head

四、統計網站日誌中PV量

統計Apache/Nginx日誌中某一天的PV量  <統計日誌>
# grep '27/Jul/2017' mysqladmin.cc-access_log |wc -l
14519

統計Apache/Nginx日誌中某一天不一樣IP的訪問量 <統計日誌>
# grep '27/Jul/2017' mysqladmin.cc-access_log |awk '{ips[$1]++};END{for(i in ips){print i,ips[i]} }' |sort -k2 -rn |head

# grep '07/Aug/2017' access.log |awk '{ips[$1]++};END{for(i in ips){print i,ips[i]} }' |awk '$2>100' |sort -k2 -rn

名詞解釋:

==網站瀏覽量(PV)==
名詞:PV=PageView (網站瀏覽量)
說明:指頁面的瀏覽次數,用以衡量網站用戶訪問的網頁數量。屢次打開同一頁面則瀏覽量累計。用戶每打開一個頁面便記錄1次PV。

名詞:VV = Visit View(訪問次數)
說明:從訪客來到您網站到最終關閉網站的全部頁面離開,計爲1次訪問。若訪客連續30分鐘沒有新開和刷新頁面,或者訪客關閉了瀏覽器,則被計算爲本次訪問結束。

獨立訪客(UV)
名詞:UV= Unique Visitor(獨立訪客數)
說明:1天內相同的訪客屢次訪問您的網站只計算1個UV。

獨立IP(IP)
名詞:IP=獨立IP數
說明:指1天內使用不一樣IP地址的用戶訪問網站的數量。同一IP不管訪問了幾個頁面,獨立IP數均爲1

————————————本文到此爲止,感謝閱讀——————————————

相關文章
相關標籤/搜索