Linux之awk詳解

'#Gun awk的相關用法

awk的工作原理

一次讀取一行文本,按輸入分隔符進行切片,切成多個組成部分,將每片直接保存在內建的變量中,$1,$2,$3....,引用指定的變量,可以顯示指定斷,或者多個斷。如果需要顯示全部的,需要使用$0來引用??梢詫蝹€片斷進行判斷,也可以對所有斷進行循環(huán)判斷。其默認分隔符為空格

awk的基本用法格式

  • awk [options] 'program' FILE......
    • 語句之間用分號分隔
  • [options]
    • -F : 指明輸入時用到的字段分隔符
    • -v var=VALUE : 自定義變量
      • 在awk中變量的引用不需要加$,而是直接引用

awk用法的簡要介紹

  • 第一種模式
    • awk [options] 'scripts' file1,file2.....

      在這種模式中,scripts主要是命令的堆砌,對輸入的文本行進行處理,通過命令print,printf或是輸出重定向的方式顯示出來,這里經常用到的知識點是:awk的內置變量,以及命令print和printf的使用

  • 第二種模式
    • awk [options] 'PATTERN{action}' file,file2.....

      在這種模式中,最重要的是5種模式和5種action的使用,以及awk的數組的使用和內置函數

第一種模式

1、print
  • 1、各項目之間使用逗號隔開,而輸出時則以空白字符分隔
  • 2、輸出的Item可以為字符串或數值,當前記錄的字段(如$1)、變量或awk的表達式,數值會先轉換為字符串,而后再輸出
  • 3、print命令后面的Item可以省略,此時其功能相當于print $0,因此,如果想輸出空白行,則需要使用print""
  • 4、如果引用變量$1或其他的,是不能使用引號引起來
2、內置變量
  • FS : input field seperator,輸入的分隔符,默認為空白字符

  • OFS: output field seperator,輸出的分隔符,默認為空白字符

  • RS : input record seperator,輸入的換行符

  • ORS: output record seperator,輸出時的換行符

  • NF : number of field ,字段個數

    • awk '{print NF}' /etc/fstab :打印每行的最后一個字段為第幾個字段,這里是數量引用,不是對應的值引用
    • awk '{print $NF}' /etc/fstab : 打印每行中的最后一個字段
  • NR : number of record,文件中的行數

    • awk '{print NR}' /etc/fstab: 打印行號,其會個行號都顯示
    • awk 'END{print NR}' /etc/fstab: 顯示文本的總行數,其只是在文本處理完成后,只顯示一次行號
    • awk '{print NR}' file1 file2 : 會每把所有文檔進行總的編號,而不是單獨對文件進行編號
  • FNR : 對每個文件進行行數單獨編號

    • awk '{print FNR}' file1 file2 : 會對每個文件的行數進行單獨的編號顯示
  • FILENAME : awk命令所處理的文件的名稱

    • awk '{print FILENAME}' file1 : 顯示當前文件名,但會每行顯示一次
    • awk 'END{print FILENAME}' file1 : 顯示當前文件名,但只會顯示一次
  • ARGC : 命令行中參數的個數,其awk命令也算一個參數

    • awk 'END{print ARGC}' /etc/fstab : 顯示共有幾個參數
  • ARGV : 其是一個數組,保存的是命令行所給定的各參數

    • awk 'END{print ARGV[0]}' /etc/fstab : 顯示第一個參數,默認第一個參數個awk命令本身
3、自定義變量
  • -v var=VALUE : 在選項位置定義

  • awk 'BEGIN{test="hello";print test}' : 在program中定義

      變量在program中定義時,需要使用引號引起來
    
4、printf命令
  • 其格式化輸出:printf FORMAT,item1,item2....
要點:
  • 1、其與print命令最大不同是,printf需要指定format
  • 2、printf后面的字串定義內容需要使用雙引號引起來
  • 3、字串定義后的內容需要使用","分隔,后面直接跟Item1,item2....
  • 4、format用于指定后面的每個item的輸出格式
  • 5、printf語句不會自動打印換行符,\n
格式符
  • %c: 顯示字符的ASCII碼
  • %d,%i : 顯示十進制整數
  • %e,%E: 科學計數法數值顯示
  • %f : 顯示為浮點數
  • %g,%G: 以科學數法或浮點形式顯示數值
  • %s: 顯示字符串
  • %u: 無符號整數
  • %%: 顯示%號自身,相當于轉義
修飾符
  • N : 顯示寬度
  • - : 左對齊(默認為右對齊)
  • + : 顯示數值符號
  • 示例:
    • awk -F: '{printf "%s\n",$1}' /etc/fstab
    • awk -F: '{printf "username: %s,UID:%d\n",$1,$3}' /etc/passwd
    • awk -F: '{printf "username: %-20s shell: %s\n",$1,$NF}' /etc/passwd
輸出重定向
  • print items > "output-file"

  • print items >> "output-file"

  • print items | command

      特殊文件描述符:
      /dev/stdin :標準輸入
      /dev/stdout:標準輸出
      /dev/stderr:錯誤輸出
      /dev/fd/N : 某特定文件描述符,如/dev/stdin就相當于/dev/fd/0
    
    • 示例
    • awk -F: '{printf "%-15s %i\n",$1,$3 > "/dev/stderr"}' /etc/passwd

第二種模式

  • awk [option] 'PATTERN{action}' file1,file2....
PATTERN的使用
  • REGEXP:正則表達式,格式為/regular expression/,僅處理能夠被此處模式匹配到的行

      awk '/^UUID/{print $1}' /etc/fstab
      awk '!/^UUID/{print $1}' /etc/fstab
    
  • relational expression:表達式,其值非0或為非空字符時滿足條件,用運算符~(匹配)和!~(不匹配)

      $1 ~ /foo/ 或者 $1 == "magedu"
    
  • Ranges : 指定匹配范圍,格式為/pat1/,/pat2/

      awk -F: '{NR>=2&&<=10){print $1}' /etc/passwd
      awk -F: /^root/,/^myuser/{print $1}' /etc/passwd
      注意:不支持直接給出數字的格式
    
  • BEGIN/END模式 : 特殊模式,僅在awk命令執(zhí)行前運行一次或結束前運行一次

      awk -F: 'BEGIN{print "Username    ID    Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}' /etc/passwd :先打印一個表頭
      awk -F: 'BEGIN{print "username   ID     Shell"}{printf "%-10s%-10s%-20s\n",$1,$3,$7}END{print "end of report."} /etc/passwd :打印一個表尾
    
  • Empty(空模式):匹配任意輸入行

      /正則表達式/:使用通配符的擴展集。
      關系表達式:可以用下面運算符表中的關系運算符進行操作,可以是字符串或數字的比較,如$2>$1選擇第二個字段比第一個字段長的行。
      模式匹配表達式:
      模式,模式:指定一個行的范圍。該語法不能包括BEGIN和END模式。
      BEGIN:讓用戶指定在第一條輸入記錄被處理之前所發(fā)生的動作,通??稍谶@里設置全局變量。
      END:讓用戶在最后一條輸入記錄被讀取之后發(fā)生的動作。
    
常見的Action
  • 1)Expressions
    1. Control statements :if while等
    1. Compound statements:組合語句
    1. Input statements
    1. Output statements
控制語句
1、if-else
  • 語法:if (condition){then-body} else{[else-body]}
  • 示例:
    • awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
    • awk -F: '{if($3>=1000){printf "Common user: %s\n",$1} else {printf "root or sysuser: %s\n",$1}}' /etc/passwd
    • awk -F: '{if($NF=="/bin/bash")print $1}' /etc/passwd
    • awk -F: '{if(NF>5) print $0}' /etc/fstab
    • df -h | awk -F[%] '/^/dev/{print $1}' | awk {if($NF>=20) print $1}'
    • awk -F: '{if($1=="root") print $1,"Admin";else print $1, "Common User"}' /etc/passwd
    • awk -F: '{if($1=="root") printf "%-15s: %s\n",$1,"Admin";else printf "%-15s: %s\n",$1, "Common user"}' /etc/passwd
    • awk -F: -v sum=0 '{if($3>=500) sum++}END{print sum}' /etc/passwd : 統(tǒng)計用戶ID大于500的有多少行
    • awk -F: -v OFS="\t" '{if($3<=999)printf "Sys user:\t%-15s ID is :%d\n", $1,$3;else{printf "Common user:\t%-15s ID is :%d\n",$1,$3}}' /etc/passwd :可以使用\t制表符控制 輸出格式
2、while:用于循環(huán)字段的
  • 語法:while (condition){statement1;statment2;....}
  • 示例:
    • awk '/^[[:space:]]*linux16/{print}' /boot/grub2/grub.cfg
    • awk '/^[[:space:]]*linux16/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub2.cfg :對每個字段進行字符個數統(tǒng)計
    • awk '/^[[:space:]]]*linux16/{i=1;while(i<=NF){if(length($i)<=7)print $i,length($i);i++}}' /etc/grub2.cfg
    • awk -F: '{i=1;while(i<=3){print $i;i++}}' /etc/passwd:打印用戶名、密碼占位符、ID
    • awk -F: '{i=1;while(i<=NF){if(length($i)>=4){print $i};i++}}' /etc/passwd : 字段大小于等于4的都顯示
3、do-while
  • 語法:do {statement1,statement2,....} while (dondition)
  • 示例:
    • awk -F: '{i=1;do{print $i;i++}while(i<=3)}' /etc/passwd :打印用戶名、密碼占位符、UID
4、for
  • 語法:for(variable assignment;condition;iteration process){ statement1,statement2,...}

  • 示例:

    • awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
    • awk -F: '{for(i=1;i<=3;i++)print $i}' /etc/passwd
    • awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd
5、for循環(huán)還可以用來遍歷數組元素
  • 語法:for (i in array) {statement1,statement2,....}
  • 示例
    • awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd
    • awk ‘{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}’ /etc/fstab
    • awk '/^UUID/{filesystem[$3]++}END{for (i in filesystem) {print i,filesystem[i]}}' /etc/fstab :統(tǒng)計/etc/fstab中各文件系統(tǒng)的次數
    • netstat -tan | awk '/^tcp>/{state[$NF]++}END{for(i in state) {print i,state[i]}}' : 統(tǒng)計各連接狀態(tài)的次數
    • awk '{ip[$1]++} END {for (i in ip) {print i,ip[i]}}' /var/log/httpd/access_log : 統(tǒng)計訪問日志中各IP的訪問次數
6、case
  • 語法:switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}
7、break和continue
  • break [n]
  • continue : 進入下一個字段
8、next
  • 功能:提前結束本行文本的處理,并接著處理下一行
  • 示例:
    • awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd
    • awk -F: ‘{if ($3%2!=0) next;print $1,$3}’ /etc/passwd
awk的操作符
  • 1、算術操作符
    • -x : 負值
    • +x : 轉換為數值
    • x^y :
    • x**y : 次方
    • x*y : 乘法
    • x/y : 除法
    • x+y :
    • x-y :
    • x%y :
  • 2、字符串操作符
    • 只有一個,而且不用寫出來,用于實現(xiàn)字符串連接
  • 3、賦值操作符
    • =
    • +=
    • -=
    • *=
    • /=
    • %=
    • ^=
    • **=
    • ++
    • --

    需要注意的是,如果某模式為=號,此時使用/=/可能會有語法錯誤,就以/[=]/替代

  • 4、布爾值

    awk中,任何非0值或非空字符串都為真,反之為假

  • 5、比較操作符
    • x<y
    • x<=y
    • x>y
    • x>=y
    • x==y
    • x!=y
    • x~y
    • x!~y
  • 6、邏輯操作符
    • &&
    • ||
    • !
  • 7、函數調用
    • `func_name(argu1,argu2,....)
  • 8、條件表達式
    • selector?if-true-expression:if-false-expression
      • awk -F: ‘{$3>=1000?usertype=“Common user”:usertype=“Sysadmin or sysUser”;printf “%15s:%-s\n",$1,usertype}’ /etc/passwd
awk的數組
1、定義數組的注意事項
  • 1、可使用任意字符串,字符串要使用雙引號
  • 2、如果某數組元素事先不存在,在引用時awk會自動創(chuàng)建此元素,并將其初始化為空串
  • 3、要遍歷數組中的每個元素,要使用for循環(huán)
    • for(var in array){statement1,.....}

        注意:var用于引用數組時,是引用的下標,而不是元素值
      
定義數組的格式
  • awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print week["mon"]}'
示例:
  • 統(tǒng)計netstat -tan中各狀態(tài)的次數

    • netstat -tan | awk '/^tcp>/{state[$NF]++}END{for(i in state) {print i,state[i]}}’
      • 每出現(xiàn)一被/^tcp/模式匹配到的行,數組S[$NF]就加1,NF為當前匹配到的行的最后一個字段,此處用其值做為數組S的元素索引
  • 統(tǒng)計www服務的訪問日志中IP數量

    • awk ‘{ip[$1]++} END {for (i in ip) {print i,ip[i]}}’ /var/log/httpd/access_log
  • 統(tǒng)計/etc/fstab文件中每個文件系統(tǒng)類型出現(xiàn)的次數

    • awk '/^UUID/{filesystem[$3]++}END{for (i in filesystem) {print i,filesystem[i]}}' /etc/fstab
  • 統(tǒng)計指定文件中單詞的出現(xiàn)次數

    • awk ‘{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}’ /etc/fstab
  • 統(tǒng)計出/etc/passwd文件中shell的種類和個數

    • awk -F: '{shell[$NF]++}END{for(A in shell){print A,shell[A]}}' /etc/passwd
    重點解析一下這條命令:
      首先,shell[$NF]++這個語句。其中的$NF所代表的是一個字符串,即shell的類型。也就是說在這個數組中的元素名稱是shell的名稱。而后面的++是對這個數組進行賦值。因為在/etc/passwd 這個文件中的shell類型只有兩種,即/bin/bash和/sbin/nologin。也就是說這個數組就只有兩個元素,并且它的值是不斷被更新的。其次,語句for(A in shell)是設定了A是數組shell中的坐標變量,即是A是元素的名稱,shell[A]是數組的值。
刪除數組變量
  • delete array[index]
awk的內置函數
  • split(string, array [, fieldsep [, seps ] ])

    • 功能:將string表示的字符串以fieldsep為分隔符進行分隔,并將分隔后的結果保存至array為名的數組中;數組下標為從0開始的序列;

    • 示例:

        netstat -tn | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for(i in count)print i,count[i]}'
        netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50
      
  • length([string])

    • 功能:返回string字串中字符的個數
  • substr(string, start [, length])

    • 功能:取string字符串中的子串,從start開始,取length個;start從1開始計數
  • system(command)

    • 功能:執(zhí)行系統(tǒng)command并將結果返回至awk命令
  • systime()

    • 功能:取系統(tǒng)當前時間
  • tolower(s)

    • 功能:將s中的所有字母轉為小寫
  • toupper(s)

    • 功能:將s中的所有字母轉為大寫
番外篇!?。?!
    Linux Web服務器網站故障分析常用的命令

    系統(tǒng)連接狀態(tài)篇:
    1.查看TCP連接狀態(tài)
    netstat -nat |awk ‘{print $6}’|sort|uniq -c|sort -rn

    netstat -n | awk ‘/^tcp/ {++S[$NF]};END {for(a in S) print a, S[a]}’ 或
    netstat -n | awk ‘/^tcp/ {++state[$NF]}; END {for(key in state) print key,"\t",state[key]}’
    netstat -n | awk ‘/^tcp/ {++arr[$NF]};END {for(k in arr) print k,"t",arr[k]}’

    netstat -n |awk ‘/^tcp/ {print $NF}’|sort|uniq -c|sort -rn

    netstat -ant | awk ‘{print $NF}’ | grep -v ‘[a-z]‘ | sort | uniq -c

     

    2.查找請求數請20個IP(常用于查找攻來源):

    netstat -anlp|grep 80|grep tcp|awk ‘{print $5}’|awk -F: ‘{print $1}’|sort|uniq -c|sort -nr|head -n20

    netstat -ant |awk ‘/:80/{split($5,ip,":");++A[ip[1]]}END{for(i in A) print A[i],i}’ |sort -rn|head -n20

    3.用tcpdump嗅探80端口的訪問看看誰最高

    tcpdump -i eth0 -tnn dst port 80 -c 1000 | awk -F"." ‘{print $1"."$2"."$3"."$4}’ | sort | uniq -c | sort -nr |head -20

    4.查找較多time_wait連接

    netstat -n|grep TIME_WAIT|awk ‘{print $5}’|sort|uniq -c|sort -rn|head -n20

    5.找查較多的SYN連接

    netstat -an | grep SYN | awk ‘{print $5}’ | awk -F: ‘{print $1}’ | sort | uniq -c | sort -nr | more

    6.根據端口列進程

    netstat -ntlp | grep 80 | awk ‘{print $7}’ | cut -d/ -f1
    netstat -tnlp | awk '/22/{split($NF,port,"/");d[port[1]]++}END{for(i in d)print i}'

     

    網站日志分析篇1(Apache):

    1.獲得訪問前10位的ip地址

    cat access.log|awk ‘{print $1}’|sort|uniq -c|sort -nr|head -10
    cat access.log|awk ‘{counts[$(11)]+=1}; END {for(url in counts) print counts[url], url}’

    2.訪問次數最多的文件或頁面,取前20

    cat access.log|awk ‘{print $11}’|sort|uniq -c|sort -nr|head -20

    3.列出傳輸最大的幾個exe文件(分析下載站的時候常用)

    cat access.log |awk ‘($7~/.exe/){print $10 " " $1 " " $4 " " $7}’|sort -nr|head -20

    4.列出輸出大于200000byte(約200kb)的exe文件以及對應文件發(fā)生次數

    cat access.log |awk ‘($10 > 200000 && $7~/.exe/){print $7}’|sort -n|uniq -c|sort -nr|head -100

    5.如果日志最后一列記錄的是頁面文件傳輸時間,則有列出到客戶端最耗時的頁面

    cat access.log |awk ‘($7~/.php/){print $NF " " $1 " " $4 " " $7}’|sort -nr|head -100

    6.列出最最耗時的頁面(超過60秒的)的以及對應頁面發(fā)生次數

    cat access.log |awk ‘($NF > 60 && $7~/.php/){print $7}’|sort -n|uniq -c|sort -nr|head -100

    7.列出傳輸時間超過 30 秒的文件

    cat access.log |awk ‘($NF > 30){print $7}’|sort -n|uniq -c|sort -nr|head -20

    8.統(tǒng)計網站流量(G)

    cat access.log |awk ‘{sum+=$10} END {print sum/1024/1024/1024}’

    9.統(tǒng)計404的連接

    awk ‘($9 ~/404/)’ access.log | awk ‘{print $9,$7}’ | sort

    10. 統(tǒng)計http status

    cat access.log |awk ‘{counts[$(9)]+=1}; END {for(code in counts) print code, counts[code]}'
    cat access.log |awk '{print $9}'|sort|uniq -c|sort -rn

    10.蜘蛛分析,查看是哪些蜘蛛在抓取內容。

    /usr/sbin/tcpdump -i eth0 -l -s 0 -w - dst port 80 | strings | grep -i user-agent | grep -i -E 'bot|crawler|slurp|spider'

    網站日分析2(Squid篇)按域統(tǒng)計流量

    zcat squid_access.log.tar.gz| awk '{print $10,$7}' |awk 'BEGIN{FS="[ /]"}{trfc[$4]+=$1}END{for(domain in trfc){printf "%st%dn",domain,trfc[domain]}}'

    數據庫篇
    1.查看數據庫執(zhí)行的sql

    /usr/sbin/tcpdump -i eth0 -s 0 -l -w - dst port 3306 | strings | egrep -i 'SELECT|UPDATE|DELETE|INSERT|SET|COMMIT|ROLLBACK|CREATE|DROP|ALTER|CALL'

    系統(tǒng)Debug分析篇
    1.調試命令
    strace -p pid
    2.跟蹤指定進程的PID
    gdb -p pid

9.gawk應用實現(xiàn)

(1)內建變量;
1)字段和數據行分隔符變量

    數據字段變量:
        允許你使用美元符號($)和數據字段在數據行中位置對應的數值來引用該數據行中的字段

    字段分隔符:
        默認情況下,字段分隔符是一個空白字符,也就是空格符或制表符(tab)。在命令行下使用命令行參數-F或者在gawk程序中使用特殊的內置變量FS來更改字段分隔符

    變量匯總:
        FIELDWIDTHS         由空格分隔開的定義了每個數據字段確切寬度的一列數字
        FS                  輸入字段分隔符
        RS                  輸入數據行分隔符
        OFS                 輸出字段分隔符
        ORS                 輸出數據行分隔符

    變量FS和OFS定義了gawk如何處理數據流中的數據字段

        [root@ahui ~]# cat data1
        data11,data12,data13,data14,data15
        data21,data22,data23,data24,data25
        data31,data32,data33,data34,data35
        
        [root@ahui ~]# awk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' data1
        data11-data12-data13
        data21-data22-data23
        data31-data32-data33

    FIELDWIDTHS變量允許你讀取數據行,而不用字段分隔符來劃分字段。一旦設置了FIELDWIDTHS變量,gawk就會忽略FS變量,而根據提供的字段寬度大小來計算字段

        [root@ahui ~]# cat data1b
        1005.3847887.37
        133-2.374893.23
        23987.8237488.9
        
        [root@ahui ~]# awk 'BEGIN{FIELDWIDTHS="3 5 2 5"} {print $1,$2,$3,$4}' data1b
        100 5.384 78 87.37
        133 -2.37 48 93.23
        239 87.82 37 488.9

    默認情況下,gawk將RS和ORS設為換行符,但是有時會碰到數據流中字段占了多行的情況。
    經典的例子就是包含地址和電話號碼的數據,其中地址和電話號碼各占一行

        [root@ahui ~]# cat data2
        Riley Mullen
        123 main street
        Chicago, IL 60601
        (312)555-2378

        Frank Williams
        123 main street
        Chicago, IL 60601
        (312)234-3253

        Haley Snell
        123 main street
        Chicago, IL 60601
        (312)123-2374
        
        [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2
        Riley Mullen  (312)555-2378
        Frank Williams (312)234-3253
        Haley Snell (312)123-2374

        只需把FS變量設置成換行符。這就表明數據流中的每行都是一個單獨的字段,每行上的所有數據都屬于同一個字段。
        接著只需把RS變量設置成空字符串,然后在數據行間留一個空白行。gawk會把每個空白行當做一個數據行分隔符

2)數據變量:
    ARGC        當前命令行參數個數
    ARGIND      當前文件在ARGV中的位置
    ARGV        包含命令行參數的數組
    CONVFMT     數字的轉換格式(參見printf語句);默認值為%.6 g
    ENVIRON     當前shell環(huán)境變量及其值組成的關聯(lián)數組
    ERRNO       當讀取或關閉輸入文件發(fā)生錯誤時的系統(tǒng)錯誤號
    FILENAME    用作gawk輸入數據的數據文件的文件名
    FNR         當前數據文件中的數據行數
    IGNORECASE  設成非零值時,忽略gawk命令中出現(xiàn)的字符串的字符大小寫
    NF          數據文件中的字段總數
    NR          已處理的輸入數據行數目
    OFMT        數字的輸出格式;默認值為%.6 g
    RLENGTH     由match函數所匹配的子字符串的長度
    RSTART      由match函數所匹配的子字符串的起始位置

    相關實例:

        [root@ahui ~]# gawk 'BEGIN{print ARGC,ARGV[1]}' data1
        2 data1

        注意:ARGC變量表明命令行上有兩個參數,這包括gawk命令和data1參數(記住,程序腳本并不算參數)。
             ARGV數組從代表該命令的索引0開始,第一個數組值是gawk命令后的第一個命令行參數。


        [root@ahui ~]# gawk '
        > BEGIN{
        > print ENVIRON["HOME"]
        > print ENVIRON["PATH"]
        > }'
        /root
        /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

        注意:ENVIRON變量使用關聯(lián)數組來提取shell環(huán)境變量。關聯(lián)數組用文本作為數組的索引值,而不用數值。可用這種方式來從shell中提取任何環(huán)境變量的值來在gawk程序中使用。

        [root@ahui ~]# gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd
        root:/bin/bash
        bin:/sbin/nologin
        daemon:/sbin/nologin
        adm:/sbin/nologin
        lp:/sbin/nologin
        sync:/bin/sync
        shutdown:/sbin/shutdown
        halt:/sbin/halt
        mail:/sbin/nologin
        uucp:/sbin/nologin
        operator:/sbin/nologin
        games:/sbin/nologin
        gopher:/sbin/nologin
        ftp:/sbin/nologin
        nobody:/sbin/nologin
        dbus:/sbin/nologin
        usbmuxd:/sbin/nologin
        vcsa:/sbin/nologin
        rpc:/sbin/nologin
        rtkit:/sbin/nologin
        avahi-autoipd:/sbin/nologin
        abrt:/sbin/nologin
        rpcuser:/sbin/nologin
        nfsnobody:/sbin/nologin
        haldaemon:/sbin/nologin
        gdm:/sbin/nologin
        ntp:/sbin/nologin
        apache:/sbin/nologin
        saslauth:/sbin/nologin
        postfix:/sbin/nologin
        pulse:/sbin/nologin
        sshd:/sbin/nologin
        tcpdump:/sbin/nologin
        ahui:/bin/bash

        注意:NF變量允許你指定數據行中的最后一個數據字段,即NF變量含有數據文件中最后一個數據字段的數字值。可以在它前面加個美元符將它用作字段變量。

        [root@ahui ~]# gawk '
        > BEGIN{FS=","}
        > {print $1, "FNR="FNR, "NR="NR}
        > END{print "There were",NR,"records processed"}' data1 data1
        data11 FNR=1 NR=1
        data21 FNR=2 NR=2
        data31 FNR=3 NR=3
        data11 FNR=1 NR=4
        data21 FNR=2 NR=5
        data31 FNR=3 NR=6
        There were 6 records processed

        注意:FNR變量的值在gawk處理第二個文件時被重置了,而NR變量則在進入第二個數據文件后繼續(xù)計數
             注意變量賦值的語法結構:"FNR="FNR。引號位置錯了會出現(xiàn)語法錯誤

(2)自定義變量
1)在腳本中給變量賦值

    [root@ahui ~]# gawk '
    > BEGIN{
    > testing="This is a test"
    > print testing
    > }'
    This is a test

    注意:gawk編程語言包含了用來處理數字值的標準數學操作符,其中包括求余符號(%)和冪運算符號(^或**)

2)在命令行上給變量賦值

    [root@ahui ~]# cat script1
    BEGIN{FS=","}
    {print $n}
    
    [root@ahui ~]# gawk -f script1 n=2 data1
    data12
    data22
    data32

    這個特性允許你改變腳本的行為而不需要修改實際的腳本代碼。
    使用命令行參數來定義變量值會有個問題。在你設置了變量后,這個值在代碼的BEGIN部分不可用

    [root@ahui ~]# cat script2
    BEGIN{print "The starting value is",n; FS=","}
    {print $n}
    
    [root@ahui ~]# gawk -f script2 n=3 data1
    The starting value is
    data13
    data23
    data33

    可以用-v命令行參數來解決這個問題。它允許你指定在BEGIN代碼部分之前設定的變量。在命令行上,-v命令行參數必須放在腳本代碼之前

    [root@ahui ~]# gawk -v n=3 -f script2 data1
    The starting value is 3
    data13
    data23
    data33

(3)處理數組

gawk編程語言使用關聯(lián)數組來提供數組功能,關聯(lián)數組跟數字數組不同之處在于它的索引值可以是任意文本字符串。每個索引字符串都必須是唯一的,并唯一地標識賦給它的數據元素。

1)定義數組變量
    數組變量賦值的格式如下;
        var[index] = element
    其中var是變量名,index是關聯(lián)數組的索引值,element是數據元素值。

    [root@ahui ~]# gawk 'BEGIN{
    > capital["Illinois"] = "Springfiled"
    > print capital["Illinois"]
    > }'
    Springfiled

2)遍歷數組變量
    可以用for語句的一種特殊形式:
        for (var in array)
        {
            statements
        }
    這個for語句會在每次將關聯(lián)數組array的下一個索引值賦給變量var時,執(zhí)行一遍statements。重要的是記住這個變量是索引值而不是數據元素值。

        [root@ahui ~]# gawk 'BEGIN{
        > var["a"] = 1
        > var["b"] = 2
        > var["c"] = 3
        > var["d"] = 4
        > for (test in var)
        > {
        > print "Index:",test," - value:",var[test]
        > }
        > }'
        Index: a  - value: 1
        Index: b  - value: 2
        Index: c  - value: 3
        Index: d  - value: 4

3)刪除數組變量
    從關聯(lián)數組中刪除數組索引要用一個特別的命令:
        delete array[index]

    刪除命令會從數組中刪除關聯(lián)索引值和相關的數據元素值
        [root@ahui ~]# gawk 'BEGIN{
        > var["a"] = 1
        > var["g"] = 2
        > for (test in var)
        > {
        > print "Index:",test," - value:",var[test]
        > }
        > delete var["g"]
        > print "---"
        > for (test in var)
        > print "Index:",test," - Value:",var[test]
        > }'
        Index: a  - value: 1
        Index: g  - value: 2
        ---
        Index: a  - Value: 1

(4)使用模式

1)正則表達式
    在使用正則表達式時,正則表達式必須出現(xiàn)在它要控制的程序腳本的左花括號前:

        [root@ahui ~]# gawk 'BEGIN{FS=","} /11/{print $1}' data1
        data11

2)匹配操作符
    匹配操作符(matching operate)允許將正則表達式限定在數據行中的特定數據字段。匹配操作符是波浪線(~)。
        $1 ~ /^data/
    $1變量代表數據行中的第一個數據字段,這個表達式會過濾出第一個字段以文本data開頭的所有數據行。

        [root@ahui ~]# gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
        data21,data22,data23,data24,data25  

    也可以使用!符號來排除正則表達式的匹配:
        $1 !~ /expression/

        [root@ahui ~]# gawk  '$1 !~ /root/{print $1,$NF}' /etc/passwd
        bin:x:1:1:bin:/bin:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin
        daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
        adm:x:3:4:adm:/var/adm:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin
        lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
        sync:x:5:0:sync:/sbin:/bin/sync sync:x:5:0:sync:/sbin:/bin/sync
        shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
        halt:x:7:0:halt:/sbin:/sbin/halt halt:x:7:0:halt:/sbin:/sbin/halt
        mail:x:8:12:mail:/var/spool/mail:/sbin/nologin mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
        uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
        games:x:12:100:games:/usr/games:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin
        gopher:x:13:30:gopher:/var/gopher:/sbin/nologin gopher:x:13:30:gopher:/var/gopher:/sbin/nologin
        ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
        nobody:x:99:99:Nobody:/:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin
        dbus:x:81:81:System bus:/:/sbin/nologin
        usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
        vcsa:x:69:69:virtual owner:/dev:/sbin/nologin
        rpc:x:32:32:Rpcbind Daemon:/var/cache/rpcbind:/sbin/nologin
        rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin rtkit:x:499:497:RealtimeKit:/proc:/sbin/nologin
        avahi-autoipd:x:170:170:Avahi Stack:/var/lib/avahi-autoipd:/sbin/nologin
        abrt:x:173:173::/etc/abrt:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin
        rpcuser:x:29:29:RPC User:/var/lib/nfs:/sbin/nologin
        nfsnobody:x:65534:65534:Anonymous User:/var/lib/nfs:/sbin/nologin
        haldaemon:x:68:68:HAL daemon:/:/sbin/nologin
        gdm:x:42:42::/var/lib/gdm:/sbin/nologin gdm:x:42:42::/var/lib/gdm:/sbin/nologin
        ntp:x:38:38::/etc/ntp:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin
        apache:x:48:48:Apache:/var/www:/sbin/nologin apache:x:48:48:Apache:/var/www:/sbin/nologin
        saslauth:x:498:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin
        postfix:x:89:89::/var/spool/postfix:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin
        pulse:x:497:496:PulseAudio Daemon:/var/run/pulse:/sbin/nologin
        sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
        tcpdump:x:72:72::/:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin
        ahui:x:500:500:ahuihaerbin:/home/ahui:/bin/bash ahui:x:500:500:ahuihaerbin:/home/ahui:/bin/bash

3)數學表達式

    可以使用任意的普通股數學比較表達式
        x == y:值x等于y
        x <= y:
        x < y
        x >= y
        x > y

    可以對文本數據使用表達式,但是它跟正則表達式不同,表達式必須完全匹配。

        [root@ahui ~]# gawk -F, '$1 == "data"{print $1}' data1
        
        [root@ahui ~]# gawk -F, '$1 == "data11"{print $1}' data1
        data11

(5)結構化命令
1)if語句
格式:
if (condition)
statement1

if (condition) statement1

    [root@ahui ~]# cat data4
    10
    5
    13
    50
    34
    
    [root@ahui ~]# gawk '{
    > if ($1 > 20)
    > {
    >   x = $1 * 2
    >   print x
    > }
    > }' data4
    100
    68

    gawk的if語句也支持else子句,允許在if語句條件不成立的情況下執(zhí)行一條或多條語句

    [root@ahui ~]# gawk '{
    > if ($1 > 20)
    > {
    >   x = $1 * 2
    >   print x
    > } else
    > {
    >   x = $1 / 2
    >   print x
    > }}' data4
    5
    2.5
    6.5
    100
    68

    也可以在單行上使用else子句,但必須在if語句部分使用分號:

        if (condition) statement1; else statement2

    [root@ahui ~]# gawk '{if ($1 > 20) print $1 * 2; else print $1 / 2}' data4
    5
    2.5
    6.5
    100
    68


2)while語句

    while語句的格式:

        while (condition)
        {
        statements
        }

    while循環(huán)允許遍歷一組數據,并檢查結束迭代的條件

    [root@ahui ~]# gawk '{
    total = 0
    i = 1
    while (i < 4)
    {
       total += $i
       i++
    }
    avg = total / 3
    print "Average:" ,avg
    }' data5
    Average: 128.333
    Average: 137.667
    Average: 176.667

    [root@ahui ~]# gawk '{
    > total = 0
    > i = 1
    > while (i < 4)
    > {
    >   total += $i
    >   if (i == 2)
    >      break
    >   i++
    > }
    > avg = total / 2
    > print "The average of the first two data elements is:",avg
    > }' data5
    The average of the first two data elements is: 125
    The average of the first two data elements is: 136.5
    The average of the first two data elements is: 157.5

3)do-while語句
    do-while語句類似于while語句,但會在檢查條件語句之前執(zhí)行命令。格式為:
        do
        {
            statement
        } while (condition)

    這種格式保證了語句會在條件被評估之前至少執(zhí)行一次

4)for語句
    gawk支持C風格的for循環(huán):
        for( variable assignment; condition; iteration process)

(6)格式化打印
注意到print語句在gawk如何顯示數據上并未提供多少控制。你能做的大概只是控制輸出字段分隔符(OFS)。

因此要用到格式化打印,我們需要printf命令。格式:
    printf "format string", var1, var2...

format string是格式化輸出地關鍵。它會用到文本元素和格式化指定符來具體指定如何呈現(xiàn)格式化輸出。

格式化指定符采用如下格式:
    %[modifier]control-letter
其中control-letter是指明顯示什么類型數據值的單字符碼,而modifier定義了另一個可選的格式化特性。

    c           將一個數作為ASCII字符顯示
    d           顯示一個整數值
    i           顯示一個整數值(跟d一樣)
    e           用科學計數法顯示一個數
    f           顯示一個浮點數
    g           用科學計數法或浮點數中較短的顯示
    o           顯示一個八進制值
    s           顯示一個文本字符串
    x           顯示一個十六進制值
    X           顯示一個十六進制值,但用大寫字母A-F

除了控制字母外,還有3種修飾符可以用來進一步控制輸出

    width:
        指定了輸出字段最小寬度的數字值。如果輸出短于這個值,printf會向右對齊,并用空格來填充這段空間。如果輸出比指定的寬度還要長,它就會覆蓋width值。
    prec:
        指定了浮點數中小數點后面位數的數字值,或者文本字符串中顯示的最大字符數。
    -(減號):
        減號指明在向格式化空間中放入數據時采用左對齊而不是右對齊

        [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%s %s\n", $1, $4}' data2
        Riley Mullen  (312)555-2378
        Frank Williams (312)234-3253
        Haley Snell (312)123-2374

    注意:我們需要自己手動在printf命令的末尾添加換行符來生成新行。沒加的話,printf命令會繼續(xù)用同一行來打印后續(xù)輸出。

    如果你需要用幾個單獨的printf命令來在同一行上打印多個輸出,它會非常有用:

        [root@ahui ~]# gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1
        data11 data21 data31

    接著我們用修飾符來格式化第一個字符串值:

        [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%16s  %s\n",$1, $4}' data2
            Riley Mullen  (312)555-2378
          Frank Williams  (312)234-3253
             Haley Snell  (312)123-2374

    通過添加一個值為16的修飾符,我們強制第一個字符串的輸出采用16位字符。默認情況下,printf命令使用右對齊來將數據放到格式化空間中。要改成左對齊,只要給修飾符加一個減號就可以了。

        [root@ahui ~]# gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s  %s\n", $1, $4}' data2
        Riley Mullen      (312)555-2378
        Frank Williams    (312)234-3253
        Haley Snell       (312)123-2374

(7)自定義函數
1)定義函數
要定義自己的函數,你必須使用function關鍵字:
function name([variables])
{
statements
}

    函數名必須能夠唯一標識函數

2)使用自定義函數
    在定義函數時,它必須出現(xiàn)在所有代碼塊之前(包括BEGIN代碼塊)。

        [root@ahui ~]# gawk '
        > function myprint()
        > {
        >     printf "%-16s - %s\n", $1, $4
        > }
        > BEGIN{FS="\n"; RS=""}
        > {
        >     myprint()
        > }' data2
        Riley Mullen     - (312)555-2378
        Frank Williams   - (312)234-3253
        Haley Snell      - (312)123-2374

3)創(chuàng)建函數庫
    首先創(chuàng)建一個存儲所有gawk函數的文件

        [root@ahui ~]# cat funclib
        function myprint()
        {
            printf "%-16s - %s\n", $1, $4
        }
        function myrand()
        {
            return int(limit * rand())
        }
        function printthird()
        {
            printf $3
        }

    注意:不能將-f命令行參數和內聯(lián)gawk腳本放到一起使用,不過可以在同一個命令行中使用多個-f參數

        [root@ahui ~]# cat script4
        BEGIN{ FS="\n"; RS="" }
        {
            myprint()
        }

        [root@ahui ~]# gawk -f funclib -f script4 data2
        Riley Mullen     - (312)555-2378
        Frank Williams   - (312)234-3253
        Haley Snell      - (312)123-2374
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • awk:報告生成器,格式化文本輸出 內容: awk介紹 awk基本用法 awk變量 awk格式化 awk操作符 a...
    BossHuang閱讀 1,565評論 0 9
  • awk介紹awk變量printf命令:實現(xiàn)格式化輸出操作符awk patternawk actionawk數組aw...
    哈嘍別樣閱讀 1,743評論 0 4
  • 本章主要學習內容awk介紹 ?awk基本用法 ?awk變量 ?awk格式化 ?awk操作符 ?awk條件判斷 ?a...
    楠人幫閱讀 1,377評論 0 8
  • awk: grep,sed,awk grep:文本過濾 sed:文本編輯 awk:文本格式化工具; 1 什么是aw...
    木林森閱讀 1,913評論 0 16
  • awk命令的基本使用 [root@shellscript ~]# head -n 3 /etc/passwd | ...
    古寒飛閱讀 1,116評論 0 2

友情鏈接更多精彩內容