1. 程式人生 > 實用技巧 >文字三劍客之awk用法

文字三劍客之awk用法

第1章 AWK命令

1.1 awk命令解釋

awk是一種程式語言,用於在linux/unix下對文字和資料進行處理。資料可以來自標準輸入(stdin)、一個或多個檔案,或其它命令的輸出。它支援使用者自定義函式和動態正則表示式等先進功能,是linux/unix下的一個強大程式設計工具。它在命令列中使用,但更多是作為指令碼來使用。awk有很多內建的功能,比如陣列、函式等,這是它和C語言的相同之處,靈活性是awk最大的優勢。

1)程式語言 三個人寫的 A和W和K 三個人 GNU
2)查詢匹配檔案內容
3)格式化輸出結果printf
4)統計資料
5)支援 for while if 陣列等

    [root@oldboyedu-lnb ~]# ll /usr/bin/awk         # GNU AWK

    lrwxrwxrwx. 1 root root 4 Jul 15 15:06 /usr/bin/awk -> gawk

1.2 awk對檔案資料進行統計

awk主要對哪些檔案進行資料統計

1)日誌檔案(服務日誌 SSHD NGINX MySQL 自研發的服務) 
2)系統配置檔案
3)常規普通檔案

1.3 awk命令格式和選項

  1.3.1 語法形式

awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)

   1)模式 匹配查詢字串的過程

   2)awk的工作 必須在花括號內

   3)只有模式沒有動作 預設會執行print輸出操作 # py print echo shell程式設計

   4)如果沒有模式(找誰)、預設的是對所有行進行操作

常用命令選項
-F fs   fs指定輸入分隔符,fs可以是字串或正則表示式,如-F:
-v var=value   賦值一個使用者定義變數,將外部變數傳遞給awk
-f scripfile  從指令碼檔案中讀取awk命令
-m[fr] val   對val值設定內在限制,-mf選項限制分配給val的最大塊數目;-mr選項限制記錄的最大數目。
這兩個功能是Bell實驗室版awk的擴充套件功能,在標準awk中不適用。

  1.3.2 awk模式

     awk '模式{動作}' file

    awk '找誰{幹啥}' file

     cat file|awk '模式{動作}'

模式可以是以下任意一個

/正則表示式/:使用萬用字元的擴充套件集。
 關係表示式:使用運算子進行操作,可以是字串或數字的比較測試。
 模式匹配表示式:用運算子~(匹配)和~!(不匹配)。
 BEGIN語句塊、pattern語句塊、END語句塊:

  1.3.3 操作

操作由一個或多個命令、函式、表示式組成,之間由換行符或分號隔開,並位於大括號內,主要部分是:

1.4 awk指令碼基本結構

awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file

一個awk指令碼通常由:BEGIN語句塊、能夠使用模式匹配的通用語句塊、END語句塊3部分組成,這三個部分是可選的。任意一個部分都可以不出現在指令碼中,指令碼通常是被單引號雙引號中,例如:

awk 'BEGIN{ i=0 } { i++ } END{ print i }' filename awk "BEGIN{ i=0 } { i++ } END{ print i }" filename

1.5 awk的工作原理

awk 'BEGIN{ commands } pattern{ commands } END{ commands }'

    第一步:執行BEGIN{ commands }語句塊中的語句;
    第二步:從檔案或標準輸入(stdin)讀取一行,然後執行pattern{ commands }語句塊,它逐行掃描檔案,從第一行到最後一行重複這個過程,直到檔案全部被讀取完畢。
    第三步:當讀至輸入流末尾時,執行END{ commands }語句塊。

BEGIN語句塊

在awk開始從輸入流中讀取行之前被執行,這是一個可選的語句塊,比如變數初始化、列印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中。

END語句塊

在awk從輸入流中讀取完所有的行之後即被執行,比如列印所有行的分析結果這類資訊彙總都是在END語句塊中完成,它也是一個可選語句塊。

pattern語句塊

中的通用命令是最重要的部分,它也是可選的。如果沒有提供pattern語句塊,則預設執行{ print },即列印每一個讀取到的行,awk讀取的每一行都會執行該語句塊。

例子:
[root@centos7 ~]# echo -e "A line 1\nA line 2" | awk 'BEGIN{ print "Start" } { print } END{ print "End" }'
Start
A line 1
A line 2
End

當使用不帶引數的print時,它就列印當前行,當print的引數是以逗號進行分隔時,列印時則以空格作為定界符。在awk的print語句塊中雙引號是被當作拼接符使用,例如:

示例:
[root@centos7 ~]# echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3; }'
v1 v2 v3

雙引號拼接使用

[root@centos7 ~]# echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1"="var2"="var3; }'
v1=v2=v3

{ }類似一個迴圈體,會對檔案中的每一行進行迭代,通常變數初始化語句(如:i=0)以及列印檔案頭部的語句放入BEGIN語句塊中,將列印的結果等語句放在END語句塊中。

1.6 awk內建變數(預定義變數)

說明:[A][N][P][G]表示第一個支援變數的工具,[A]=awk、[N]=nawk、[P]=POSIXawk、[G]=gawk
$n 當前記錄的第n個欄位,比如n為1表示第一個欄位,n為2表示第二個欄位。 $0 這個變數包含執行過程中當前行的文字內容。
[N] ARGC 命令列引數的數目。
[G] ARGIND 命令列中當前檔案的位置(從0開始算)。
[N] ARGV 包含命令列引數的陣列。
[G] CONVFMT 數字轉換格式(預設值為%.6g)。
[P] ENVIRON 環境變數關聯陣列。
[N] ERRNO 最後一個系統錯誤的描述。
[G] FIELDWIDTHS 欄位寬度列表(用空格鍵分隔)。
[A] FILENAME 當前輸入檔案的名。
[P] FNR 同NR,但相對於當前檔案。
[A] FS 欄位分隔符(預設是任何空格)。
[G] IGNORECASE 如果為真,則進行忽略大小寫的匹配。
[A] NF 表示欄位數,在執行過程中對應於當前的欄位數。
[A] NR 表示記錄數,在執行過程中對應於當前的行號。
[A] OFMT 數字的輸出格式(預設值是%.6g)。
[A] OFS 輸出欄位分隔符(預設值是一個空格)。
[A] ORS 輸出記錄分隔符(預設值是一個換行符)。
[A] RS 記錄分隔符(預設是一個換行符)。
[N] RSTART 由match函式所匹配的字串的第一個位置。
[N] RLENGTH 由match函式所匹配的字串的長度。
[N] SUBSEP 陣列下標分隔符(預設值是34)。 

環境準備

     1  Wu      Waiwai          70271111        :250:80:75

     2  Liu     Bingbing        41117483        :250:100:175

     3  Wang    Xiaoai          3515064655      :50:95:135

     4  Zi      Gege            1986787350      :250:168:200

     5  Li      Youjiu          918391635       :175:75:300

     6  Lao     Nanhai          918391635       :250:100:176

1.7 awk 中的變數

awk中的變數(類似於作業系統自帶的變數 LANG PATH PS1 $?上一條命令執行的返回結果)

1) NR 行號 把檔案的所有行按照順序都會記錄到NR變數中

2) $0 $1 表示檔案的所有和檔案的第n

3) , 逗號在awk中表示空格

4) NF 儲存了每一行的最後一列的列號

1.7.1 awk取行 NR 行號

NR的表示符號
== 等於   在大部分的命令中一個等號是賦值 變數的意思
!= 不等於
>  大於
<  小於
>= 大於等於
<= 小於等於
&& 並且 兩端同時成立
|| 或者 

1.7.2 輸出檔案中的第三行

awk 'NR==3' file
[root@oldboyedu-lnb ~]# awk 'NR==3' oldboy.txt
Wang    Xiaoai          3515064655      :50:95:135

1.7.3 輸出檔案中大於3的行

    [root@oldboyedu-lnb ~]# awk 'NR>3' oldboy.txt

    Zi      Gege            1986787350      :250:168:200

    Li      Youjiu          918391635       :175:75:300

    Lao     Nanhai          918391635       :250:100:176

1.7.4 輸出檔案不等於5的行

   [root@oldboyedu-lnb ~]# awk 'NR!=5' oldboy.txt

    Wu      Waiwai          70271111        :250:80:75

    Liu     Bingbing        41117483        :250:100:175

    Wang    Xiaoai          3515064655      :50:95:135

    Zi      Gege            1986787350      :250:168:200

    Lao     Nanhai          918391635       :250:100:176

1.7.5 輸出檔案大於等於3的行

    [root@oldboyedu-lnb ~]# awk 'NR>=3' oldboy.txt

    Wang    Xiaoai          3515064655      :50:95:135

    Zi      Gege            1986787350      :250:168:200

    Li      Youjiu          918391635       :175:75:300

    Lao     Nanhai          918391635       :250:100:176

1.7.6 輸出檔案內容等於2並且大於1的行

   [root@oldboyedu-lnb ~]# awk 'NR==2 && NR>1' oldboy.txt

    Liu     Bingbing        41117483        :250:100:175      

    [root@oldboyedu-lnb ~]# awk 'NR==2 || NR==5' oldboy.txt

    Liu     Bingbing        41117483        :250:100:175

    Li      Youjiu          918391635       :175:75:300

    [root@oldboyedu-lnb ~]# awk 'NR==2 || NR==10' oldboy.txt

    Liu     Bingbing        41117483        :250:100:175      

1.7.7 查詢檔案的3-5行

[root@oldboyedu-lnb ~]# awk 'NR>2&& NR<6' oldboy.txt
Wang    Xiaoai           3515064655      :50:95:135
Zi         Gege             1986787350      :250:168:200
Li         Youjiu            918391635        :175:75:300

1.7.8 查詢檔案的2-6行

[root@oldboyedu-lnb ~]# awk 'NR>=2&& NR<=6' oldboy.txt
Liu     Bingbing        41117483        :250:100:175
Wang    Xiaoai          3515064655      :50:95:135
Zi      Gege            1986787350      :250:168:200
Li      Youjiu          918391635       :175:75:300
Lao     Nanhai          918391635       :250:100:176              

1.8 awk取列

1.8.1 變數

              $0 awk在執行過程中把每一行都賦值給$0  $0表示所有檔案內容

              $1 檔案中的第一列

              $2 檔案中的第二列

              $n 檔案中的第n列  n代表數字

              預設的列是以tab鍵或空格來分隔

1.8.1.1 $0 輸出檔案所有內容

[root@oldboyedu-lnb ~]# awk '{print $0}' oldboy.txt

    Wu      Waiwai          70271111        :250:80:75

    Liu     Bingbing        41117483        :250:100:175

    Wang    Xiaoai          3515064655      :50:95:135

    Zi      Gege            1986787350      :250:168:200

    Li      Youjiu          918391635       :175:75:300

    Lao     Nanhai          918391635       :250:100:176

1.9 取檔案中的列數

語法格式:

 awk '{print $n}' file   # 輸出檔案中的第n列

    #PS:查詢行

    awk '模式' file

    #PS:查詢列

    awk '{動作}' file       # 對檔案中所有的行都進行動作處理

  1.9.1 輸出檔案中的第一列

[root@oldboyedu-lnb ~]# awk '{print $1}' oldboy.txt
Wu
Liu
Wang
Zi
Li
Lao

  1.9.2 輸出檔案中的第一列和最後一列 使用逗號分隔 逗號是awk中的變數

[root@oldboyedu-lnb ~]# awk '{print $1,$4}' oldboy.txt
Wu :250:80:75
Liu :250:100:175
Wang :50:95:135
Zi :250:168:200
Li :175:75:300
Lao :250:100:176

  1.9.3 輸出檔案中的最後一列 變數 NF 表示每一行最後一列的總列數

  [root@oldboyedu-lnb ~]# cat oldboy.txt
   Wu      Waiwai        70271111        :250:80:75
   Liu     Bingbing       41117483        :250:100:175
   Wang    Xiaoai        3515064655    :50:95:135
   Zi      Gege            1986787350     :250:168:200
   Li      Youjiu            918391635      :175:75:300
   Lao    Nanhai          918391635      :250:100:176
   Aug  5 12:01:01 oldboyedu-lnb systemd: Started Session 26 of user root.
   alex   test
  [root@oldboyedu-lnb ~]# awk '{print NF}' oldboy.txt
    4
    4
    4
    4
    4
    4
    11
    2

  1.9.4 輸出檔案中的最後一列

[root@oldboyedu-lnb ~]# awk '{print $NF}' oldboy.txt
:250:80:75
:250:100:175
:50:95:135
:250:168:200
:175:75:300
:250:100:176
root.
test
[root@oldboyedu-lnb ~]# awk '{print $(NF-1)}' oldboy.txt
70271111
41117483
3515064655
1986787350
918391635
918391635
user
alex
awk '{print $3}' oldboy.txt ======= awk '{print $(4-1)}' oldboy.txt
[root@oldboyedu-lnb ~]# awk 'BEGIN{print 10*1000/100+2^3}'      # 瞭解
108

PS:awk中的動作都是變數 取消變數使用雙引號

awk動作中可以輸出任何自己想要的字串 必須加雙引號

awk動作中不在雙引號中的字串都被視為變數

        [root@oldboyedu-lnb ~]# awk '{print $1"hehe"$4}' oldboy.txt

        Wuhehe:250:80:75

        Liuhehe:250:100:175

        Wanghehe:50:95:135

        Zihehe:250:168:200

        Lihehe:175:75:300

        Laohehe:250:100:176

        [root@oldboyedu-lnb ~]#

        [root@oldboyedu-lnb ~]# awk '{print $1" "$4}' oldboy.txt

        Wu :250:80:75

        Liu :250:100:175

        Wang :50:95:135

        Zi :250:168:200

        Li :175:75:300

        Lao :250:100:176

        [root@oldboyedu-lnb ~]# awk '{print $1"-----"$4}' oldboy.txt

        Wu-----:250:80:75                  

        Liu-----:250:100:175

        Wang-----:50:95:135

        Zi-----:250:168:200

        Li-----:175:75:300

        Lao-----:250:100:176

        [root@oldboyedu-lnb ~]# awk '{print $1" awk "$4}' oldboy.txt

        Wu awk :250:80:75

        Liu awk :250:100:175

        Wang awk :50:95:135

        Zi awk :250:168:200

        Li awk :175:75:300

        Lao awk :250:100:176

        [root@oldboyedu-lnb ~]# awk '{print $1"\t"$4}' oldboy.txt

        Wu  :250:80:75

        Liu :250:100:175

        Wang    :50:95:135

        Zi  :250:168:200

        Li  :175:75:300

        Lao :250:100:176

        [root@oldboyedu-lnb ~]# awk '{print $1"\n"$4}' oldboy.txt

        Wu

        :250:80:75

        Liu

  1.9.5 取出/etc/passwd中的第一列

   -F 指定分割符(可指定任意的) 預設以tab鍵和空格來分隔

   -F的第一種寫法:

       [root@oldboyedu-lnb ~]# cat passwd.txt

        root:x:0:0:root:/root:/bin/bash

        bin:x:1:1:bin:/bin:/sbin/nologin

        [root@oldboyedu-lnb ~]# awk -F ":" '{print $1}' passwd.txt

        root

        bin

  -F 的第二種寫法:

[root@oldboyedu-lnb ~]# awk -F: '{print $1}' passwd.txt
root
bin     
[root@oldboyedu-lnb ~]# awk -F: '{print $1,$2,$3,$4,$5,$6,$7}' passwd.txt
root x 0 0 root /root /bin/bash
bin x 1 1 bin /bin /sbin/nologin

  1.9.6 取出passwd中的第6列但是不要/

awk -F 指定多個分隔符

語法格式:

 awk -F ":/"
 awk -F "[:/]"

案例1: 使用":/" 視作一個整體作為分隔符

     [root@oldboyedu-lnb ~]# cat passwd.txt

      root:x:0:0:root:/root:/bin/bash

      bin:x:1:1:bin:/bin:/sbin/nologin

     [root@oldboyedu-lnb ~]# awk -F ":/" '{print $1 }' passwd.txt

      root:x:0:0:root

      bin:x:1:1:bin

     [root@oldboyedu-lnb ~]# awk -F ":/" '{print $2 }' passwd.txt

      root

      bin

     [root@oldboyedu-lnb ~]# awk -F ":/" '{print $3 }' passwd.txt

      bin/bash

      sbin/nologin

案例2: 使用"[:/]" 或者: 或者/ 作為分隔符

       [root@oldboyedu-lnb ~]# cat passwd.txt

        root:x:0:0:root:/root:/bin/bash

        bin:x:1:1:bin:/bin:/sbin/nologin

        [root@oldboyedu-lnb ~]# awk -F "[:/]" '{print $9}' passwd.txt

        bin

        sbin

案例3:使用"[:/]+"

        [root@oldboyedu-lnb ~]# cat passwd.txt
        root:x:0:0:root:/root:/bin/bash
        bin:x:1:1:bin:/bin:/sbin/nologin
        [root@oldboyedu-lnb ~]# awk -F "[:/]+" '{print $6}' passwd.txt
        root
        bin
       [root@oldboyedu-lnb ~]# echo :://...---alex:..oldboy|awk -F "[:/.]+" '{print $2}'
        ---alex
        [root@oldboyedu-lnb ~]#
       [root@oldboyedu-lnb ~]# echo :://...---alex:..oldboy|awk -F "[:/.-]+" '{print $2}'
        alex
        [root@oldboyedu-lnb ~]# echo :://...---alex:..oldboy|awk -F "[:/.-]+" '{print $3}'
        oldboy
        [root@oldboyedu-lnb ~]# echo :://...---alex:..oldboy|awk -F ":/.-" '{print $2}'
        [root@oldboyedu-lnb ~]# echo :://...---alex:..oldboy|awk -F "[:/.-]" '{print $2}'
        [root@oldboyedu-lnb ~]# echo :://...---alex:..oldboy|awk -F "[:/.-]+" '{print $2}'
        alex  

問題1: 出現連續的分隔符

  [root@oldboyedu-lnb ~]# cat passwd.txt

    root::x:0:0:root:/root:/bin/bash

    bin:x:1:1:bin:/bin:/sbin/nologin

    [root@oldboyedu-lnb ~]# awk -F: '{print $6}' passwd.txt

    root

    /bin

問題2: 以單引號或者任意符號作為分隔符

   [root@oldboyedu-lnb ~]# awk -F\' '{print $3}' passwd.txt
   test
   [root@oldboyedu-lnb ~]# awk -F "[']" '{print $3}' passwd.txt
   test
   [root@oldboyedu-lnb ~]# cat passwd.txt
   root:'x:0:0:root:/root:/bin/bash'test
   bin:x:1:1:bin:/bin:/sbin/nologin
   [root@oldboyedu-lnb ~]# awk -F0 '{print $1}' passwd.txt
   root:'x:
[root@oldboyedu-lnb ~]# cat test.txt
alex
[root@oldboyedu-lnb ~]# awk -Fl '{print $1}' test.txt
a
[root@oldboyedu-lnb ~]# awk -Fl '{print $2}' test.txt
ex

  1.9.7 輸出檔案內容的第二行的第六列

語法格式:

awk '模式{動作}' file

[root@oldboyedu-lnb ~]# awk -F: 'NR==2{print $6}' passwd.txt
/bin   
[root@oldboyedu-lnb ~]# awk -F: 'NR>1{print $NF}' passwd.txt
/sbin/nologin
[root@oldboyedu-lnb ~]# awk -F: 'NR>1&&NR==2{print $NF}' passwd.txt
/sbin/nologin

  1.9.8 使用模糊匹配

語法格式:

 grep '過濾的內容' file
sed -n '/匹配的內容/' file
awk '//' file
awk '//,//' file

    1.9.8.1 查詢包含root的行

[root@oldboyedu-lnb ~]# awk '/root/' passwd.txt
root:'x:0:0:root:/root:/bin/bash'test

    1.9.8.2 查詢包含root或者nologin的行

[root@oldboyedu-lnb ~]# awk '/root|nologin/' passwd.txt
root:'x:0:0:root:/root:/bin/bash'test
bin:x:1:1:bin:/bin:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin

    1.9.8.3 區間匹配

[root@oldboyedu-lnb ~]# awk '/root/,/ntp/' passwd.txt
 root:'x:0:0:root:/root:/bin/bash'test
 bin:x:1:1:bin:/bin:/sbin/nologin
 ntp:x:38:38::/etc/ntp:/sbin/nologin

  1.9.9 判斷 可以是字串的比對 也可以按照列數判斷(數字)

  字串比對
  格式: $1=="root"   等於root說明成功 成功後執行print動作 &&
  格式2: $3>100     第三列的每一個行的數字都和100進行比較 如果沒有動作預設輸出 大於100的所有行
  比較符:
   ==       # 字串使用== 和!=
              >

             <

             !=

             >=

             <=
 

[root@oldboyedu-lnb ~]# awk -F: '$1=="root"{print $2}' passwd.txt
     'x
[root@oldboyedu-lnb ~]# cat count.txt
 alex 1 2 3 4 5 6
 oldboy 10 20 30 40
 lidao  100 200 300 400
[root@oldboyedu-lnb ~]# awk '$3>10' count.txt
 oldboy 10 20 30 40
 lidao  100 200 300 400
 [root@oldboyedu-lnb ~]# awk '$3>10{print $1}' count.txt
 oldboy
  lidao

[root@oldboyedu-lnb ~]# awk '$2==100' count.txt
lidao  100 200 300 400
[root@oldboyedu-lnb ~]# awk '$2==100{print $NF}' count.txt
 400
[root@oldboyedu-lnb ~]# awk '$2>=1&& $2<400' count.txt
 alex 1 2 3 4 5 6
 oldboy 10 20 30 40
 lidao  100 200 300 400                          
[root@oldboyedu-lnb ~]# awk '$2==1|| $4==300' count.txt
 alex 1 2 3 4 5 6
 lidao  100 200 300 400
[root@oldboyedu-lnb ~]# awk '$2==1|| $4==600' count.txt
 alex 1 2 3 4 5 6

  1.9.10 NR行號

[root@oldboyedu-lnb ~]# awk '{print NR}' passwd.txt
1
2
3
4
[root@oldboyedu-lnb ~]# awk '{print NR,$0}' passwd.txt
1 root:'x:0:0:root:/root:/bin/bash'test
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 ntp:x:38:38::/etc/ntp:/sbin/nologin
4 alexdsx:x:1001:1001::/home/alexdsx:/bin/bash

根據檔案的行數來執行相應的動作 動作可以和檔案無關

[root@oldboyedu-lnb ~]# awk -F: '{print "ok"}' passwd.txt
        ok

        ok

        ok

        ok
[root@oldboyedu-lnb ~]# awk -F: '{a++}' passwd.txt
[root@oldboyedu-lnb ~]# awk -F: '{a++}END{print a}' passwd.txt
4

  1.9.11 BEGIN 在動作之前做什麼操作

語法格式:

awk 'BEGIN{動作}{執行檔案的動作}' file

[root@oldboyedu-lnb ~]# awk 'BEGIN{print "開始了"}{print "ok"}' passwd.txt

        開始了

        ok

        ok

        ok

        ok

        [root@oldboyedu-lnb ~]# awk '{print 10*10}' passwd.txt

        100

        100

        100

        100

  1.9.12 END 在執行完所有的操作後執行END動作

語法格式:

awk '{執行檔案的動作}END{print "執行完檔案後的動作"}' file

[root@oldboyedu-lnb ~]# awk '{print 10*10}END{print "end......"}' passwd.txt
100
100
100
100
end......

BEGIN和END結合

語法:

awk 'BEGIN{執行檔案前的動作}{執行檔案的動作}END{執行完檔案後的動作}' file

[root@oldboyedu-lnb ~]# awk -F: 'BEGIN{print "開始執行"}{print $1}END{print "執行完成"}'passwd.txt
開始執行
root
bin
ntp
alexdsx
執行完成

案例

[root@oldboyedu-lnb ~]# awk -F: '$3>0&&$3<1000{a++}END{print a}' /etc/passwd
23

  1.9.13 按照匹配規則匹配

sed -n '/^root/p' passwd.txt

grep '^root' passwd.txt

[root@oldboyedu-lnb ~]# awk '/^root/' passwd.txt
root:'x:0:0:root:/root:/bin/bash'test            
[root@oldboyedu-lnb ~]# grep 'test$' passwd.txt
root:'x:0:0:root:/root:/bin/bash'test
[root@oldboyedu-lnb ~]# sed -n '/test$/p' passwd.txt
root:'x:0:0:root:/root:/bin/bash'test
[root@oldboyedu-lnb ~]# awk '/test$/' passwd.txt
root:'x:0:0:root:/root:/bin/bash'test           
[root@oldboyedu-lnb ~]# awk -F: '$1 ~ /^ntp/' passwd.txt
ntp:x:38:38::/etc/ntp:/sbin/nologin
[root@oldboyedu-lnb ~]# awk -F: '$7 ~ /bash$/' passwd.txt
alexdsx:x:1001:1001::/home/alexdsx:/bin/bash
[root@oldboyedu-lnb ~]# awk -F: '$7 ~ /nologin$/' passwd.txt
bin:x:1:1:bin:/bin:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin

awk -F: '$1 ~ /in$/{動作}' file

[root@oldboyedu-lnb ~]# awk -F: '$7 ~ /nologin$/{print "ok"}' passwd.txt
ok
ok
[root@oldboyedu-lnb ~]# awk -F: '$7 ~ /nologin$/{print $1}' passwd.txt
bin
ntp
[root@oldboyedu-lnb ~]# awk -F: '$7 ~ /nologin$/{print $NF}' passwd.txt
/sbin/nologin
/sbin/nologin

awk 匹配取反 瞭解

[root@oldboyedu-lnb ~]# awk -F: '$7 !~ /nologin$/' passwd.txt
root:'x:0:0:root:/root:/bin/bash'test
alexdsx:x:1001:1001::/home/alexdsx:/bin/bash

案例:

已知系統使用者 管理員UID 0 虛擬使用者 1-999 普通使用者UID999+
/etc/passwd 中以冒號分隔的第三列是UID
如何使用awk統計出每類使用者的數量

第一步: 獲取沒類使用者的總數

[root@oldboyedu-lnb ~]# awk -F: '$3==0' /etc/passwd
[root@oldboyedu-lnb ~]# awk -F: '$3>0 && $3<1000' /etc/passwd
[root@oldboyedu-lnb ~]# awk -F: '$3>999' /etc/passwd   

第二步: 統計輸出到螢幕上的行數 wc -l

[root@oldboyedu-lnb ~]# awk -F: '$3>0 && $3<1000' /etc/passwd|less -N
[root@oldboyedu-lnb ~]# awk -F: '$3>0 && $3<1000' /etc/passwd|grep -n .
[root@oldboyedu-lnb ~]# awk -F: '$3>0 && $3<1000' /etc/passwd|cat -n
[root@oldboyedu-lnb ~]# awk -F: '$3>0 && $3<1000' /etc/passwd|wc -l
23
------------------
[root@oldboyedu-lnb ~]# awk -F: '$3>0 && $3<1000' /etc/passwd|wc -l   # 虛擬使用者
23 
[root@oldboyedu-lnb ~]# awk -F: '$3>999' /etc/passwd |wc -l            # 普通使用者
2
[root@oldboyedu-lnb ~]# awk -F: '$3==0' /etc/passwd |wc -l         # 管理員使用者
1

--------------------------擴充套件瞭解------------------------------------------

[root@oldboyedu-lnb ~]# awk -F: '$3>0 && $3<1000{a++}END{print a}' /etc/passwd
         23
[root@oldboyedu-lnb ~]# awk -F: '$3==0{a++}END{print a}' /etc/passwd
           1
[root@oldboyedu-lnb ~]# awk -F: '$3>999{a++}END{print a}' /etc/passwd
           2

  1.9.14 使用if判斷來統計使用者個數

擴充套件: for while if 陣列

[root@oldboyedu-lnb ~]# awk -F: '{if($3==0){a++}else if($3>0&&$3<1000){b++}else($3>999){c++}}END{print a,b,c}' /etc/passwd
1 23 2
[root@oldboyedu-lnb ~]# awk -F: '{if($3==0){a++}else if($3>0&&$3<1000){b++}else{c++}}END{print a,b,c}' /etc/passwd
1 23 2
 

  1.9.15 算數運算

[root@oldboyedu-lnb ~]# awk 'BEGIN{print 10*1000/100+2^3}'     # 瞭解
108

1.10 陣列

1.11 awk 小結

取行NR

取列$0 $1

NF 每行最後一列的列號 $NF 最後一列的內容

字串比對 $1==root

數字比對 $3==0 $3>=0

awk 動作中所有的字元變數 輸出內容加雙引號

, 空格

-F 分隔符 空格 tab分隔 "[:/]+" 多個字元作為分隔符

BEGIN

END

1.12 awk運算與判斷

作為一種程式設計語言所應具有的特點之一,awk支援多種運算,這些運算與C語言提供的基本相同。awk還提供了一系列內建的運算函式(如log、sqr、cos、sin等)和一些用於對字串進行操作(運算)的函式(如length、substr等等)。這些函式的引用大大的提高了awk的運算功能。作為對條件轉移指令的一部分,關係判斷是每種程式設計語言都具備的功能,awk也不例外,awk中允許進行多種測試,作為樣式匹配,還提供了模式匹配表示式~(匹配)和~!(不匹配)。作為對測試的一種擴充,awk也支援用邏輯運算子。

1.13 算術運算子

算術操作符: x+y, x-y, x*y, x/y, x^y, x%y - x:轉換為負數 +x:將字串轉換為數值 字串操作符:沒有符號的操作符,字串連線 賦值操作符: =, +=, -=, *=, /=, %=, ^=,++, -- 下面兩語句有何不同 • awk ‘BEGIN{i=0;print ++i,i}’ • awk ‘BEGIN{i=0;print i++,i}’

特殊示例:

a+=5;等價於:a=a+5

1.14 操作符

比較操作符:

==, !=, >, >=, <, <= 模式匹配符:
~:左邊是否和右邊匹配,包含
!~:是否不匹配
[root@centos7~]#awk '$0~"^root"' /etc/passwd    $0顯示的行滿足^root的正則表示式進行列印
root:x:0:0:root:/root:/bin/bash
[root@centos7~]#awk '$0!~"^root"' /etc/passwd    $0顯示的行滿足非以^root為行首的正則表示式進行列印
[root@centos7~]#awk -F: '$3==0' /etc/passwd        第三列等於0的行進行列印
root:x:0:0:root:/root:/bin/bash
[root@centos7~]#lastb | awk '$3~/[[:digit:]]/{print $3}'    $3顯示的行滿足數字的正則表達進行列印第三列
192.168.34.100
192.168.34.1
192.168.34.1
192.168.34.1
192.168.34.1
[root@centos7~]#lastb  | awk '$3 ~  /^[[:digit:]]/{print $3}'  | sort | uniq -c |awk '$1  >=3{print  $1,$2}'    顯示IP地址連線次數大於3的進行列印
[root@centos7~]#awk -F: '($3>=1000){print $1,$3}' /etc/passwd            顯示第三列大於1000的第1行和第3行。
nfsnobody 65534
liu 1000
 

操作符

邏輯操作符:與&&,或||,非!

示例:

[root@centos7~]#awk -F: '$3>=1000 && $3<=2000{print $1,$3}' /etc/passwd  顯示第3列大於1000
且小於2000的第1和第3列<br>liu 1000
[root@centos7~]#awk -F: '$3==0 || $3>=1000 {print $1,$3}' /etc/passwd   
顯示等於0和大於等於1000的第一和第三列 root 0 nfsnobody 65534 liu 1000

條件表示式(三目表示式)

selector?if-true-expression:if-false-expression

示例:

[root@centos7~]#awk -F: '{$3>=1000?name="common user":name="system user";print name,$1,$3}' /etc/passwd $3大於1000的顯示第一和第三列的name命名為common user,小於1000的,命名為system user system user root 0 system user bin 1 system user daemon 2 system user adm 3

printf命令

格式化輸出:printf “FORMAT”, item1, item2, ...

(1) 必須指定FORMAT

(2) 不會自動換行,需要顯式給出換行控制符,\n

(3) FORMAT中需要分別為後面每個item指定格式符

格式符:與item一一對應

%c:顯示字元的ASCII碼
 %d, %i:顯示十進位制整數
 %e, %E:顯示科學計數法數值
 %f:顯示為浮點數
 %g, %G:以科學計數法或浮點形式顯示數值
 %s:顯示字串
 %u:無符號整數
 %%:顯示%自身
 修飾符
 #[.#]  第一個數字控制顯示的寬度;第二個#表示小數點後精度,%3.1f
 -  左對齊(預設右對齊) %-15s
 +  顯示數值的正負符號 %+d

示例:

[root@centos7~]#awk -F: '{printf "%-20s  %-10s\n",$1,$3}' /etc/passwd     提取第1列和第3列將其進行左對齊
root            0           
bin             1        
daemon          2        
adm             3        
lp              4        
sync            5

awk PATTERN

PATTERN:根據pattern條件,過濾匹配的行,再做處理

(1)如果未指定:空模式,匹配每一行

(2) /regular expression/:僅處理能夠模式匹配到的行,需要用/ /括起來

awk '/^UUID/{print $1}' /etc/fstab

awk '!/^UUID/{print $1}' /etc/fstab

(3) relational expression: 關係表示式,結果為“真”才會被處理

真:結果為非0值,非空字串

假:結果為空字串或0值

(4)BEGIN/END模式

BEGIN{}:僅在開始處理檔案中的文字之前執行一次

END{}:僅在文字處理完成之後執行一次

示例:

[root@centos7~]#df | awk -F"[[:space:]]+|%"  '/^\/dev\/sd/{print $1,$5}'    以空白和%為界限,取出當前的IP地址和裝置:
/dev/sda2 4
/dev/sda3 1
/dev/sda1 17
ss  -nt | awk -F"[[:space:]]+|:"  '/ESTAB/{print  $6}'   取第6列的IP
ss  -nt | awk -F"[[:space:]]+|:"  '/ESTAB/{print  $(NF-2)}'  取倒數第三列的IP

relational expression: 關係表示式,結果為“真”才會被處理

真:結果為非0值,非空字串

假:結果為空字串或0值

賦值為0的示例:得出的結果是假

[root@centos7~]# awk '0{print $0}' /etc/fstab  
[root@centos7~]#echo $?
0

賦值為空的示例:得出的結果是假

[root@centos7~]# awk '""{print $0}' /etc/fstab
[root@centos7~]#echo $?
0

為非0時就會顯示結果,就為真,示例如下:

[root@centos7~]# awk '"1"{print $0}' /etc/fstab
#
# /etc/fstab
# Created by anaconda on Thu Aug 22 15:21:16 2019
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=38dd5f68-4f30-411c-b80a-0f4a60b06c6f /         xfs     defaults       0 0
UUID=4357cc0e-6ee7-4a8f-8064-d1a54bdbf17f /boot  xfs     defaults        0 0
UUID=eb4bf5e6-2645-4b1c-bda8-12c5831b81c2 /data xfs     defaults        0 0
UUID=b8c37e0b-3628-40b6-ac44-c36ca09b448f swap swap    defaults        0 0

line ranges:行範圍

startline,endline:/pat1/,/pat2/ 不支援直接給出數字格式

示例:

[root@centos7~]#awk -F: '/^root/,/^adm/{print $1}' /etc/passwd  顯示第1列root開頭的行到adm開頭的行
root
bin
daemon
adm
[root@centos7~]#awk -F: '(NR>=10&&NR<=14){print NR,$1}' /etc/passwd  顯示第10行到14行的第1列
10 operator
11 games
12 ftp
13 nobody
14 systemd-netword

特殊示例:

列印奇數行:

[root@centos7~]#seq  10 | awk  'i=!i'     第一個i為空值,為假,取反就會打印出1,第二個出來為1時,為真,取反為假,就不列印2,以此類推,得出以下結果。
1
3
5
7
9

列印偶數行:

seq 10 | sed -n '1~2n'        列印奇數行
seq 10 | sed -n '2~2n'        列印偶數行
第一種情況:seq 10 | awk -v i="a" 'i=!i'
第二種情況:[root@centos7~]#seq  10 | awk  '!(i=!i)'
2
4
6
8  
10

常用的action分類

• (1) Expressions:算術,比較表示式等

• (2) Control statements:if, while等

• (3) Compound statements:組合語句

• (4) input statements

• (5) output statements:print等

awk控制語句

{ statements;… } 組合語句

if(condition) {statements;…}

if(condition) {statements;…} else {statements;…}

while(conditon) {statments;…}

do {statements;…} while(condition)

for(expr1;expr2;expr3) {statements;…}

break

continue

delete array[index]

delete array

uexit

awk控制語句if-else

語法:if(condition){statement;…}[else statement]

if(condition1){statement1}else if(condition2){statement2}else{statement3}

使用場景:對awk取得的整行或某個欄位做條件判斷

條件判斷語句:if

格式中語句1可以是多個語句,為了方便判斷和閱讀,最好將多個語句用{}括起來。awk分枝結構允許巢狀,其格式為:

示例:

awk 'BEGIN{
test=100;
if(test>90){
print "very good";
}
else if(test>60){
print "good";
}
else{
print "no pass";
}
}'
very good


awk 'BEGIN{
test=100;
if(test>90){
print "very good";
}
else if(test>60){
print "good";
}
else{
print "no pass";
}
}'
very good

while迴圈

語法:while(condition){statement;…}

條件“真”,進入迴圈;條件“假”,退出迴圈

使用場景:

對一行內的多個欄位逐一類似處理時使用

對陣列中的各元素逐一處理時使用

示例:

顯示第一行,且統計第一行有多少個位元組,並列印每個字元。

[root@centos7~]#awk -F: 'NR==1{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/passwd
root 4
x 1
0 1
0 1
root 4
/root 5

示例:

取出字元大於等於10的行,並統計位元組數

[root@centos7~]#awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){if(length($i)>=10){print $i,length($i)};i++}}' /etc/grub2.cfg
/vmlinuz-3.10.0-957.el7.x86_64 30
root=UUID=38dd5f68-4f30-411c-b80a-0f4a60b06c6f 46
LANG=en_US.UTF-8 16
/vmlinuz-0-rescue-7a7fe51fce8c4639a5a046ac251485d0 50

示例:生成隨機1000個數字

[root@centos7~]#for i in {1..1000};do if [ $i -eq 1 ];then echo -e "$RANDOM\c" >> f1.txt;else echo -e ",$RANDOM\c" >> f1.txt;fi;done

然後在隨機數中取出最大值最小值:

[root@centos7~]#awk -F ',' '{i=2;max=$1;min=$1;while (i<=NF){if($i > max){max=$i}
else if($i <min){min=$i};i++}}END{print "max="max,"min="min}' f1.txt max=1653826510 min=8

do-while迴圈

語法:do {statement;…}while(condition)

意義:無論真假,至少執行一次迴圈體

示例:1+2..100求和

[root@centos7~]#awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'
 5050

for迴圈

語法:for(expr1;expr2;expr3) {statement;…}

常見用法:

for(variable assignment;condition;iteration process)

{for-body}

u特殊用法:能夠遍歷陣列中的元素

語法:for(var in array) {for-body}

示例:

[root@centos7~]#awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg   顯示當前行有多少個字元
linux16 7
/vmlinuz-3.10.0-957.el7.x86_64 30
root=UUID=38dd5f68-4f30-411c-b80a-0f4a60b06c6f 46
ro 2
rhgb 4
quiet 5
LANG=en_US.UTF-8 16
linux16 7
/vmlinuz-0-rescue-7a7fe51fce8c4639a5a046ac251485d0 50
root=UUID=38dd5f68-4f30-411c-b80a-0f4a60b06c6f 46
ro 2
rhgb 4
quiet 5

switch語句

語法:switch(expression) {case VALUE1 or /REGEXP/: statement1; case

VALUE2 or /REGEXP2/: statement2; ...; default: statementn}

break和continue

next:

continue語句:當 continue 語句用於 while 或 for 語句時,使程式迴圈移動到下一個迭代。

break語句:當 break 語句用於 while 或 for 語句時,導致退出程式迴圈。

示例:

awk 'BEGIN{total=0;for(i=1;i<=100;i++){if(i==50)break;total+=i};print total}'

next語句:能能夠導致讀入下一個輸入行,並返回到指令碼的頂部。這可以避免對當前輸入行執行其他的操作過程。

示例:

awk -F: '{if(NR%2==0)next;print NR,$0}'  /etc/passwd  顯示的是奇數行
awk -F: '{if(NR%2==0)print NR,$0}'  /etc/passwd    顯示偶數行

陣列應用

awk陣列

關聯陣列:array[index-expression]

uindex-expression:

• (1) 可使用任意字串;字串要使用雙引號括起來

• (2) 如果某陣列元素事先不存在,在引用時,awk會自動建立此元素,並將其值

初始化為“空串” 很重要,比較被遺忘的一個點

• (3) 若要判斷陣列中是否存在某元素,要使用“index in array”格式進行遍歷

數字做陣列索引(下標):

Array[1]="sun"

Array[2]="kai"

字串做陣列索引(下標):

Array["first"]="www"

Array["last"]="name"

Array["birth"]="1987"

使用中print Array[1]會打印出sun;使用print Array[2]會打印出kai;使用print Array["birth"]會得到1987。

示例:

[root@centos7~]#awk 'BEGIN{title["coo"]="wang";title["ceo"]="ma";print title["coo"]}'
wang

示例: 類似於去重的功能

解釋:line[$0]第一次顯示的值為空值;

然後取反就為真,列印第一個值,++會將第一次出現的值進行累加,然後取反為假,就不列印重複出現的值。

[root@centos7~]#awk '{!line[$0]++;print $0,line[$0]}' f1.txt  顯示當前的詳細過程,驗證第一次陣列賦值為空值,取反為1.
aaa 1 
sss 1 
aaa 2
ccc 1 
ccc 2
[root@centos7~]#awk '!line[$0]++' f1.txt
aaa 
sss 
ccc 
[root@centos7~]#cat f1.txt 
aaa 
sss 
aaa 
ccc
ccc

陣列for迴圈語句用法:

若要遍歷陣列中的每個元素,要使用for迴圈

for(var in array) {for-body}

注意:var會遍歷array的每個索引

示例:

[root@centos7~]#awk 'BEGIN{titel["coo"]="ma";titel["ceo"]="lige";
titel[3]="liu";for(i in titel){print i,titel[i]}}' coo ma ceo lige 3 liu

示例: 統計ip的tcp型別的次數

[root@centos7~]#ss -nt | awk -F"[[:space:]]+|:" '/ESTAB/{ip[$(NF-2)]++}END{for(i in ip){print i,ip[i]}}'
192.168.34.1 1

示例: 提取檔案系統型別和計數

[root@centos7~]#awk '/^UUID/{type[$3]++}END{for(i in type){print i type[i]}}' /etc/fstab
 swap1
 xfs3

示例:分數求平均值

[root@centos6~]#cat f1.txt
name  sex      score
a    f         90
b    m    80
c    f      50
d    m    60
[root@centos6~]#awk '!/^name/{sum[$2]+=$3;num[$2]++}END{for(i in num){print i,sum[i]/num[i]}}' f1.txt
m 70
f 70

數值處理:

rand():返回0和1之間一個隨機數

示例:隨機生成四個數值,其中int是取整數,rand()預設取出的數值小數點兩位,*100增大十倍

[root@centos6~]#awk 'BEGIN{srand();for(i=1;i<=4;i++)print int(rand()*100)}' 
5 
12 
52 
84 
取出一位100以內的隨機數:
[root@centos7~]#awk 'BEGIN{srand();print int(rand()*100)}' 
56

字串處理:

• length([s]):返回指定字串的長度

• sub(r,s,[t]):對t字串搜尋r表示模式匹配的內容,並將第一個匹配內容替換為s

示例:將第一列的:替換為-

[root@centos7~]#echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08

• gsub(r,s,[t]):對t字串進行搜尋r表示的模式匹配的內容,並全部替換為s所表

示的內容

示例:將整行:進行全部替換為-

[root@centos7~]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
2008-08-08 08-08-08

示例:將第一列全部替換為-

[root@centos7~]#echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$1)'
 2008-08-08 08:08:08

• split(s,array,[r]):以r為分隔符,切割字串s,並將切割後的結果儲存至array所

表示的陣列中,第一個索引值為1,第二個索引值為2,…

示例一:將整行以:形式進行分割,最後顯示當前行字元和序列號,其中i顯示序號,str[i]顯示處理後的字元

[root@centos7~]#echo "2008:08:08 08:08:08" | awk '{split($0,str,":")}END{for(i in str){print i,str[i]}}'
4 08
5 08
1 2008
2 08
3 08 08

示例二:整行以:為分割線,將第五列的ip地址取出並統計當前的次數

[root@centos7~]#ss -nt | awk '/^ESTAB/{split($5,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
192.168.34.1 1

自定義函式格式:

function name ( parameter, parameter, ... ) {
statements
return expression
}

示例:

cat fun.awk
function max(x,y) {
x>y?var=x:var=y   如果x>y則var=x,否則var=y
return var
}
BEGIN{a=3;b=2;print max(a,b)}
awk -f fun.awk

system命令用法:

空格是awk中的字串連線符,如果system中需要使用awk中的變數可以使用

空格分隔,或者說除了awk的變數外其他一律用""引用起來

示例一:systeml 可以呼叫awk裡邊的命令變數

[root@centos7~]#awk 'BEGIN{system("hostname")}'
centos7.localdomain 

示例二:

[root@centos7~]#awk 'BEGIN{system("ls /boot")}'
config-3.10.0-957.el7.x86_64 initramfs-3.10.0-957.el7.x86_64.img
efi symvers-3.10.0-957.el7.x86_64.gz
grub System.map-3.10.0-957.el7.x86_64
grub2 vmlinuz-0-rescue-7a7fe51fce8c4639a5a046ac251485d0
initramfs-0-rescue-7a7fe51fce8c4639a5a046ac251485d0.img  vmlinuz-3.10.0-957.el7.x86_64

將awk程式寫成指令碼,直接呼叫或執行

示例一:呼叫檔案

cat f1.awk

{if($3>=1000)print $1,$3}

awk -F: -f f1.awk /etc/passwd

cat f2.awk

示例二:呼叫指令碼檔案

#!/bin/awk -f
#this is a awk script
{if($3>=1000)print $1,$3}
加執行許可權:chmod +x f2.awk
當前目錄執行呼叫指令碼:
./f2.awk -F: /etc/passwd

向awk指令碼傳遞引數

格式:

awkfile var=value var2=value2... Inputfile

注意:在BEGIN過程中不可用。直到首行輸入完成以後,變數才可用。可以通過-v 引數,讓awk在執行BEGIN之前得到變數的值。命令列中每一個指定的變數都需要一個-v引數

示例:

cat test.awk
#!/bin/awk –f
{if($3 >=min && $3<=max)print $1,$3}
chmod +x test.awk
./test.awk -F: min=100 max=200 /etc/passwd

練習:將以下檔案內容中FQDN取出域名並根據其進行計數從高到低排序

http://mail.magedu.com/index.html

http://www.magedu.com/test.html

http://study.magedu.com/index.html

http://blog.magedu.com/index.html

http://www.magedu.com/images/logo.jpg

http://blog.magedu.com/20080102.html

答案:


[root@centos7~]#cat f1.txt
http://mail.magedu.com/index.html 
http://www.magedu.com/test.html 
http://study.magedu.com/index.html 
http://blog.magedu.com/index.html
http://www.magedu.com/images/logo.jpg 
http://blog.magedu.com/20080102.html
[root@centos7~]# awk -F"/" '{fqdn[$3]++}END{for(i in fqdn){print i,fqdn[i]}}' f1.txt | sort  -nr -k2
www.magedu.com 2 
blog.magedu.com 2
study.magedu.com 1
mail.magedu.com 1

參考文獻

https://www.cnblogs.com/struggle-1216/