Shell_awk

一、awk簡介

awk 是一種編程語言,用於在linux/unix下對文本和數據進行處理。

數據可以來自標準輸入、一個或多個文件,或其它命令的輸出。

支持用戶自定義函數和動態正則表達式等先進功能,是linux/unix
下的一個強大編程工具。

在命令行中使用,但更多是作爲腳本來使用。

awk的處理文本和數據的方式是這樣的,它逐行掃描文件,從第一行到最後一行,尋找匹配的特定模式的行,並在這些行上進行你想要的操作。如果沒有指定處理動作,則把匹配的行顯示到標準輸出(屏幕),如果沒有指定模式,則所有被操作所指定的行都被處理。

awk分別代表其作者姓氏的第一個字母。因爲它的作者是三個人,分別是Alfred Aho、Brian Kernighan、Peter Weinberger。

gawk是awk的GNU版本,它提供了Bell實驗室和GNU的一些擴展。

二、awk的兩種形式語法格式

awk [options] 'commands' filenames

awk [options] -f awk-script-file filenames

options:

-F 對於每次處理的內容,可以指定一個子定義的分隔符,默認的分隔符是空白字符(空格或 tab 鍵 )

command:

BEGIN{}                        {}               END{}

處理所有內容之前的動作       處理內容中的動作   處理所有內容之後的動作

示例

 awk 'BEGIN{print 1/2} {print "----開始處理了---"} END{print "----都處理完畢---"}' /etc/hosts
----開始處理了---
ok
ok
ok
----都處理完畢---

BEGIN{} 通常用於定義一些變量,例如 BEGIN{FS=":";OFS="---"}

========================================================

三、awk工作原理

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

(1)awk,會處理文件的每一個行,每次處理時,使用一行作爲輸入,並將這一行賦給內部變量$0,每一行也可稱爲一個記錄,以換行符結束

(2)然後,行被:(默認爲空格或製表符)分解成字段(或稱爲域),每個字段存儲在已編號的變量中,從$1開始,
最多達100個字段

(3)awk如何知道用空白字符來分隔字段的呢? 因爲有一個內部變量FS來確定字段分隔符。初始時,FS賦爲空白字符

(4)awk打印字段時,將以內置的方法使用 print 函數打印,awk 在打印出的字段間加上空格。這個空格是內部的一個變量 OFS 輸出字段的分隔符, 逗號 , 會和 OFS 進行映射,通過 OFS 可以控制這個輸出分隔符的值。

(5)awk輸出之後,將從文件中獲取另一行,並將其存儲在$0中,覆蓋原來的內容,然後將新的字符串分隔成字段並進行處理。該過程將持續到所有行處理完畢

========================================================

四、記錄與字段相關內部變量:

man awk

$0 : awk變量 $0 保存當前正在處理的行內容
NR : 當前正在處理的行是 awk 總共處理的行號。
FNR: 當前正在處理的行在其文件中的行號。
NF :每行被處理時的總字段數
FS : 輸入行時的字段分隔符,默認空格

awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd

OFS : 輸出字段分隔符,默認是一個 空格

awk 'BEGIN{FS=":"; OFS="+++"} /^root/{print $1,$2,$3,$4}' /etc/passwd

ORS 輸出記錄分隔符, 默認是換行符.

示例

將文件每一行合併爲一行

ORS默認輸出一條記錄應該回車,但是這裏是加了一個空格

awk 'BEGIN{ORS="  "} {print $0}' /etc/passwd 

五、格式化輸出:

printf 函數

awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' /etc/passwd
awk -F: '{printf "|%-15s| %-10s| %-15s|\n", $1,$2,$3}' /etc/passwd
  • %s 字符類型
  • %d 數值類型
  • %f 浮點類型
  • %-15s佔15字符 - 表示左對齊,默認是右對齊
  • printf 默認不會在行尾自動換行,加 \n

六、awk模式和動作

任何 awk 語句都由 模式動作 組成。

模式部分 決定動作語句何時觸發及觸發事件。
如果省略模式部分,動作將時刻保持執行狀態。

模式可以是任何條件語句或複合語句或正則表達式。

模式包括兩個特殊字段 BEGINEND

通常使用 BEGIN 語句設置變量、計數的初始值和打印表格的表頭字段等。

BEGIN 語句使用在任何文本瀏覽動作之前,之後文本瀏覽動作依據輸入文本開始執行。

END 語句用來在awk完成文本瀏覽動作後打印輸出文本總數和結尾狀態。

模式可以是

正則表達式:

  • 匹配記錄(整行的匹配):
awk '/^root/' /etc/passwd
awk '$0 ~ /^root/' /etc/passwd
awk '!/root/' passwd
awk '$0 !~ /^root/' /etc/passwd
  • 匹配字段:可以使用的匹配操作符(~!~
awk -F: '$1 ~ /^alice/' /etc/passwd
awk -F: '$NF !~ /bash$/' /etc/passwd
  • 比較表達式:

比較表達式採用對文本進行比較,只有當條件爲真,才執行指定的動作。
比較表達式使用關係運算符,用於比較數字與字符串。

關係運算符有
< 小於 例如 x<y
> 大於 x>y
<= 小於或等於 x<=y
== 等於 x==y
!= 不等於 x!=y
>= 大於等於 x>=y

示例

awk -F: '$3 == 0' /etc/passwd
awk -F: '$3 < 10' /etc/passwd
awk -F: '$NF == "/bin/bash"' /etc/passwd
awk -F: '$1 == "root"' /etc/passwd

df -P | grep '/' |awk '$4 > 25000'
  • 條件表達式:
awk -F: '$3>300 {print $0}' /etc/passwd
awk -F: '{ if($3>300) print $0 }' /etc/passwd
awk -F: '{ if($3>300) {print $0} }' /etc/passwd
awk -F: '{ if($3>300) {print $3} else{print $1} }' /etc/passwd
  • 算術運算:+ - * / %(模) (冪23)

可以在模式中執行計算,awk都將按浮點數方式執行算術運算

awk -F: '$3 * 10 > 500' /etc/passwd
awk -F: '{ if($3*10>500){print $0} }' /etc/passwd
  • 邏輯操作符和複合模式

&& 邏輯與, 相當於 並且
||邏輯或,相當於 或者
! 邏輯非 , 取反

awk -F: '$1~/root/ && $3<=15' /etc/passwd
awk -F: '$1~/root/ || $3<=15' /etc/passwd
awk -F: '!($1~/root/ || $3<=15)' /etc/passwd
  • 範圍模式, 符號是逗號 ,

使用語法是: 起始表達式, 終止表達式

➜  ~ awk -F: '/^bin/,/adm/ {print $0 }' /etc/passwd
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

awk 正則示例:

➜  ~ awk -F: '/^(bin|root)/' /etc/passwd
root:x:0:0:root:/root:/bin/zsh
bin:x:1:1:bin:/bin:/sbin/nologin

七、awk 腳本編程

條件判斷

if語句

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

awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd

awk -F: '{if($3>0 && $3<1000){count++;}} END{print count}' /etc/passwd 
統計系統級別用戶的數量

if...else語句

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

awk -F: '{if($3==0){print $1} else {print $7}}' /etc/passwd
awk -F: '{if($3==0) {count++} else{i++} }' /etc/passwd
awk -F: '{ if($3==0){count++} else{i++} } END{print "管理員個數: "count ; print  "系統用戶數: "i}' /etc/passwd


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

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

awk -F: '{if($3==0){i++} else if($3>999){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


循環

while

語法格式 awk '{ while(循環的條件){循環體}}'

awk 'BEGIN{ i=1; while(i<=10)   {print i; i++} }'
awk -F: '/^root/{i=1; while(i<=7){print $i; i++}}' passwd
awk '{i=1; while(i<=NF){print $i; i++}}' /etc/hosts
awk -F: '{i=1; while(i<=10) {print $0; i++}}' /etc/passwd //將每行打印10次

  • for
awk 'BEGIN{for(i=1;i<=5;i++){print i} }' //C風格for
1
2
3
4
5
awk -F: '{ for(i=1;i<=10;i++) {print $0} }' /etc/passwd //將每行打印10次
awk -F: '{ for(i=1;i<=NF;i++) {print $i} }' passwd //分別打印每行的每列

數組

# awk -F: '{username[++i]=$1} END{print username[1]}' /etc/passwd
root
# awk -F: '{username[i++]=$1} END{print username[1]}' /etc/passwd
bin
# awk -F: '{username[i++]=$1} END{print username[0]}' /etc/passwd
root

數組遍歷:

1. 按索引遍歷
2. 按元數個數遍歷

  • 按元數個數遍歷
awk -F: '{username[x++]=$1} END{for(i=0;i<x;i++) print i,username[i]}' /etc/passwd
awk -F: '{username[++x]=$1} END{for(i=1;i<=x;i++) print i,username[i]}' /etc/passwd
  • 按索引遍歷
# awk -F: '{username[x++]=$1} END{for(i in username) {print i,username[i]} }' /etc/passwd
# awk -F: '{username[++x]=$1} END{for(i in username) {print i,username[i]} }' /etc/passwd
注:變量i是索引

練習:

1\. 統計/etc/passwd中各種類型shell的數量
[root@tianyun ~]# awk -F: '{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }' /etc/passwd

2. 網站訪問狀態統計 <當前時實狀態 netstat>
[root@tianyun ~]# netstat -ant |grep :80 |awk '{access_stat[$NF]++} END{for(i in access_stat ){print i,access_stat[i]}}'
TIME_WAIT 1064
ESTABLISHED 1
LISTEN 1
[root@tianyun ~]# netstat -ant |grep :80 |awk '{access_stat[$NF]++} END{for(i in access_stat ){print i,access_stat[i]}}' |sort -k2 -n |head

[root@tianyun ~]# ss -an |grep :80 |awk '{access_stat[$2]++} END{for(i in access_stat){print i,access_stat[i]}}'
LISTEN 1
ESTAB 5
TIME-WAIT 97

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

3\. 統計當前訪問的每個IP的數量 <當前時實狀態 netstat,ss>
[root@tianyun ~]# netstat -ant |grep :80 |awk -F: '{ip_count[$8]++} END{for(i in ip_count){print i,ip_count[i]} }' |sort
172.16.130.16 289
172.16.130.33 254
172.16.130.44 158
172.16.130.99 4

[root@tianyun ~]# 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
172.16.160.77 59
172.16.160.221 16
172.16.160.17 11
172.16.160.69 8
172.16.160.51 7
172.16.160.49 7
172.16.160.13 7
172.16.160.153 3
172.16.160.79 2
172.16.160.52 2

4\. 統計Apache/Nginx日誌中某一天的PV量  <統計日誌>
[root@tianyun log]# grep '22/Mar/2017' cd.mobiletrain.org.log |wc -l
1646

5\. 統計Apache/Nginx日誌中某一天不同IP的訪問量 <統計日誌>
[root@tianyun nginx_log]# grep '07/Aug/2012' access.log |awk '{ips[$1]++} END{for(i in ips){print i,ips[i]} }' |sort -k2 -rn |head
222.130.129.42 5761
123.126.51.94 988
123.126.68.22 588
123.114.46.141 418
61.135.249.218 368
110.75.173.162 330
110.75.173.163 327
110.75.173.161 321
110.75.173.160 319
110.75.173.164 314

[root@tianyun nginx_log]# grep '07/Aug/2012' access.log |awk '{ips[$1]++} END{for(i in ips){print i,ips[i]} }' |awk '$2>100' |sort -k2 -rn
222.130.129.42 5761
123.126.51.94 988
123.126.68.22 588
123.114.46.141 418
61.135.249.218 368
110.75.173.162 330
110.75.173.163 327
110.75.173.161 321
110.75.173.160 319
110.75.173.164 314
1.202.218.67 313
110.75.173.159 311
203.208.60.80 294
221.221.207.202 266
203.208.60.82 230
203.208.60.81 209
38.111.147.83 206
61.135.249.220 187
183.39.187.86 178
61.156.142.207 129

[root@tianyun log]# awk '/22\/Mar\/2017/{ips[$1]++} END{for(i in ips){print i,ips[i]}}' sz.mobiletrain.org.log |awk '$2>100' |sort -k2 -rn|head180.153.93.44 1327
119.147.33.19 551
119.147.33.26 234
119.147.33.22 216
119.147.33.21 214
101.69.121.35 209
183.214.128.174 193
175.6.26.173 178
27.221.28.174 167
121.29.54.11 161

[root@tianyun log]# awk '/22\/Mar\/2017/{ips[$1]++} END{for(i in ips){if(ips[i]>100){print i,ips[i]}}}' sz.mobiletrain.org.log|sort -k2 -rn|head
180.153.93.44 1327
119.147.33.19 551
119.147.33.26 234
119.147.33.22 216
119.147.33.21 214
101.69.121.35 209
183.214.128.174 193
175.6.26.173 178
27.221.28.174 167
121.29.54.11 161

## 思路:將需要統計的內容(某一個字段)作爲數組的索引 ++

awk函數 統計用戶名爲4個字符的用戶:
[root@tianyun ~]# awk -F: '$1~/^....$/{count++; print $1} END{print "count is: " count}' /etc/passwd
root
sync
halt
mail
news
uucp
nscd
vcsa
pcap
sshd
dbus
jack
count is: 12

[root@tianyun ~]# awk -F: 'length($1)==4{count++; print $1} END{print "count is: "count}' /etc/passwd
root
sync
halt
mail
news
uucp
nscd
vcsa
pcap
sshd
dbus
jack
count is: 12

## awk使用外部變量:

擴展
方法一:在雙引號的情況下使用
[root@tianyun ~]# var="bash"
[root@tianyun ~]# echo "unix script" |awk "gsub(/unix/,\"$var\")"
bash script

方法一:在單引號的情況下使用
[root@tianyun ~]# var="bash"
[root@tianyun ~]# echo "unix script" |awk 'gsub(/unix/,"'"$var"'")'
bash script

[root@tianyun ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/cl-root 2.8T 246G 2.5T 9% /
tmpfs 24G 20K 24G 1% /dev/shm
/dev/sda2 1014M 194M 821M 20% /boot

[root@tianyun ~]# df -h |awk '{ if(int($5)>5){print $6":"$5} }'
/:9%
/boot:20%

[root@tianyun ~]# i=10
[root@tianyun ~]# df -h |awk '{ if(int($5)>'''$i'''){print $6":"$5} }'
/boot:20%

方法一:awk參數-v(建議)
[root@tianyun ~]# echo "unix script" |awk -v var="bash" 'gsub(/unix/,var)'
bash script

[root@tianyun ~]# awk -v user=root -F: '$1 == **user**' /etc/passwd
root:x:0:0:root:/root:/bin/bash

作業:
1\. 取得網卡IP(除ipv6以外的所有IP)
2\. 獲得內存使用情況
3\. 獲得磁盤使用情況
4\. 清空本機的ARP緩存
5\. 打印出/etc/hosts文件的最後一個字段(按空格分隔)
6\. 打印指定目錄下的目錄名
 方法一:
[root@tianyun apache_log]# arp -n |awk '/^[0-9]/{print "arp -d "$1}'
arp -d 172.16.100.10
arp -d 172.16.100.178
arp -d 172.16.100.208
arp -d 172.16.100.49
arp -d 172.16.100.250
arp -d 172.16.100.127
arp -d 172.16.100.11
arp -d 172.16.100.148
arp -d 172.16.100.128
arp -d 172.16.100.59
arp -d 172.16.100.183
[root@tianyun apache_log]# arp -n |awk '/^[0-9]/{print "arp -d "$1}' |bash

方法二:
[root@tianyun apache_log]# arp -n |awk '/^[0-9]/{print $1}' |xargs -I {} arp -d {}

[root@tianyun ~]# awk -F: '{print $7}' /etc/passwd
[root@tianyun ~]# awk -F: '{print $NF}' /etc/passwd
[root@tianyun ~]# awk -F: '{print $(NF-1)}' /etc/passwd

[root@tianyun ~]# ll |grep '^d'
drwxr-xr-x 104 root root 12288 09-22 05:37 192.168.0.48
drwxr-xr-x 2 root root 4096 10-30 15:47 apache_log
drwxr-xr-x 2 root root 4096 10-30 15:23 awk
drwxr-xr-x 2 root root 4096 10-24 09:09 Desktop
drwxr-xr-x 12 root root 4096 10-08 06:12 LEMP_Soft
drwxr-xr-x 2 root root 4096 10-24 07:38 scripts
drwxr-xr-x 6 root root 4096 2012-03-29 uplayer
drwxr-xr-x 7 root root 4096 10-23 04:53 vmware

[root@tianyun ~]# ll |grep '^d' |awk '{print $NF}'
192.168.0.48
apache_log
awk
Desktop
LEMP_Soft
scripts
uplayer
vmware


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章