Linux Shell常用技巧(四) awk

Linux Shell常用技巧(四) awk

九.  awk實用功能:

    和sed一樣,awk也是逐行掃描文件的,從第一行到最後一行,尋找匹配特定模板的行,並在這些行上運行“選擇”動作。如果一個模板沒有指定動作,這些匹配的行就被顯示在屏幕上。如果一個動作沒有模板,所有被動作指定的行都被處理。
    
   1.  awk的基本格式:
    /> awk 'pattern' filename
    /> awk '{action}' filename
    /> awk 'pattern {action}' filename
    
    具體應用方式分別見如下三個用例:
    /> cat employees
    Tom Jones         4424    5/12/66         543354
    Mary Adams      5346    11/4/63         28765
    Sally Chang       1654    7/22/54         650000
    Billy Black         1683    9/23/44         336500

    /> awk '/Mary/' employees   #打印所有包含模板Mary的行。
    Mary Adams      5346    11/4/63         28765

    #打印文件中的第一個字段,這個域在每一行的開始,缺省由空格或其它分隔符。
    /> awk '{print $1}' employees
    Tom
    Mary
    Sally
    Billy
    
    /> awk '/Sally/{print $1, $2}' employees #打印包含模板Sally的行的第一、第二個域字段。
    Sally Chang
    
    2.  awk的格式輸出:
      awk中同時提供了print和printf兩種打印輸出的函數,其中print函數的參數可以是變量、數值或者字符串。字符串必須用雙引號引用,參數 用逗號分隔。如果沒有逗號,參數就串聯在一起而無法區分。這裏,逗號的作用與輸出文件的分隔符的作用是一樣的,只是後者是空格而已。下面給出基本的轉碼序 列:

轉碼含義
\n換行
\r回車
\t製表符


    /> date | awk '{print "Month: " $2 "\nYear: ", $6}'
    Month: Oct
    Year:  2011

    /> awk '/Sally/{print "\t\tHave a nice day, " $1,$2 "\!"}' employees
                    Have a nice day, Sally Chang!

    在打印數字的時候你也許想控制數字的格式,我們通常用printf來完成這個功能。awk的特殊變量OFMT也可以在使用print函數的時候,控制數字的打印格式。它的默認值是"%.6g"----小數點後面6位將被打印。
    /> awk 'BEGIN { OFMT="%.2f"; print 1.2456789, 12E-2}'
    1.25  0.12

    現在我們介紹一下功能更爲強大的printf函數,其用法和c語言中printf基本相似。下面我們給出awk中printf的格式化說明符列表:

格式化說明符功能示例結果
%c打印單個ASCII字符。printf("The character is %c.\n",x)The character is A.
%d打印十進制數。printf("The boy is %d years old.\n",y)The boy is 15 years old.
%e打印用科學記數法表示的數。printf("z is %e.\n",z)z is 2.3e+01.
%f打印浮點數。printf("z is %f.\n",z)z is 2.300000
%o打印八進制數。printf("y is %o.\n",y)y is 17.
%s打印字符串。printf("The name of the culprit is %s.\n",$1);The name of the culprit is Bob Smith.
%x打印十六進制數。printf("y is %x.\n",y)y is f.

    注:假設列表中的變臉值爲x = A, y = 15, z = 2.3, $1 = "Bob Smith"

    /> echo "Linux" | awk '{printf "|%-15s|\n", $1}'  # %-15s表示保留15個字符的空間,同時左對齊。
    |Linux          |

    /> echo "Linux" | awk '{printf "|%15s|\n", $1}'   # %-15s表示保留15個字符的空間,同時右對齊。
    |          Linux|

    #%8d表示數字右對齊,保留8個字符的空間。
     /> awk '{printf "The name is %-15s ID is %8d\n", $1,$3}' employees
    The name is Tom             ID is     4424
    The name is Mary            ID is     5346
    The name is Sally            ID is     1654
    The name is Billy             ID is     1683

    3.  awk中的記錄和域:
    awk中默認的記錄分隔符是回車,保存在其內建變量ORS和RS中。$0變量是指整條記錄。
    /> awk '{print $0}' employees #這等同於print的默認行爲。
    Tom Jones        4424    5/12/66         543354
    Mary Adams      5346    11/4/63         28765
    Sally Chang       1654    7/22/54         650000
    Billy Black         1683    9/23/44         336500

    變量NR(Number of Record),記錄每條記錄的編號。
    /> awk '{print NR, $0}' employees
    1 Tom Jones        4424    5/12/66         543354
    2 Mary Adams      5346    11/4/63         28765
    3 Sally Chang       1654    7/22/54         650000
    4 Billy Black         1683    9/23/44         336500

    變量NF(Number of Field),記錄當前記錄有多少域。
    /> awk '{print $0,NF}' employees
    Tom Jones        4424    5/12/66          543354   5
    Mary Adams      5346    11/4/63         28765     5
    Sally Chang      1654    7/22/54          650000   5
    Billy Black        1683     9/23/44         336500    5

    #根據employees生成employees2。sed的用法可以參考上一篇blog。
    /> sed 's/[[:space:]]\+\([0-9]\)/:\1/g;w employees2' employees
    /> cat employees
    Tom Jones:4424:5/12/66:543354
    Mary Adams:5346:11/4/63:28765
    Sally Chang:1654:7/22/54:650000
    Billy Black:1683:9/23/44:336500

    /> awk -F: '/Tom Jones/{print $1,$2}' employees2  #這裏-F選項後面的字符表示分隔符。
    Tom Jones 4424

    變量OFS(Output Field Seperator)表示輸出字段間的分隔符,缺省是空格。
    />  awk -F: '{OFS = "?"};  /Tom/{print $1,$2 }' employees2 #在輸出時,域字段間的分隔符已經是?(問號)了
    Tom Jones?4424

    對於awk而言,其模式部分將控制這動作部分的輸入,只有符合模式條件的記錄纔可以交由動作部分基礎處理,而模式部分不僅可以寫成正則表達式(如上面的例子),awk還支持條件表達式,如:
    /> awk '$3 < 4000 {print}' employees
    Sally Chang     1654    7/22/54         650000
    Billy Black       1683    9/23/44         336500

    在花括號內,用分號分隔的語句稱爲動作。如果模式在動作前面,模式將決定什麼時候發出動作。動作可以是一個語句或是一組語句。語句之間用分號分隔,也可以用換行符,如:
    pattern { action statement; action statement; etc. } or
    pattern {
        action statement
        action statement
    }
    模式和動作一般是捆綁在一起的。需要注意的是,動作是花括號內的語句。模式控制的動作是從第一個左花括號開始到第一個右花括號結束,如下:
    /> awk '$3 < 4000 && /Sally/ {print}' employees
    Sally Chang     1654    7/22/54         650000

    4.  匹配操作符:
    " ~ " 用來在記錄或者域內匹配正則表達式。
    /> awk '$1 ~ /[Bb]ill/' employees      #顯示所有第一個域匹配Bill或bill的行。
    Billy Black     1683    9/23/44         336500

    /> awk '$1 !~ /[Bb]ill/' employees     #顯示所有第一個域不匹配Bill或bill的行,其中!~表示不匹配的意思。
    Tom Jones        4424    5/12/66         543354
    Mary Adams      5346    11/4/63         28765
    Sally Chang       1654    7/22/54         650000

    5.  awk的基本應用實例:
    /> cat testfile
    northwest     NW        Charles Main            3.0        .98        3        34
    western        WE        Sharon Gray            5.3        .97        5        23
    southwest     SW        Lewis Dalsass          2.7        .8          2        18
    southern       SO        Suan Chin                5.1        .95        4        15
    southeast      SE        Patricia Hemenway    4.0        .7          4        17
    eastern         EA        TB Savage                4.4        .84        5        20
    northeast      NE        AM Main Jr.               5.1        .94        3        13
    north            NO        Margot Weber          4.5        .89        5        9
    central          CT        Ann Stephens           5.7        .94        5        13

    /> awk '/^north/' testfile            #打印所有以north開頭的行。
    northwest      NW      Charles Main     3.0     .98     3       34
    northeast       NE      AM Main Jr.        5.1     .94     3       13
    north             NO      Margot Weber   4.5     .89     5       9

    /> awk '/^(no|so)/' testfile          #打印所有以so和no開頭的行。
    northwest       NW      Charles Main                3.0     .98      3       34
    southwest       SW      Lewis Dalsass              2.7     .8       2       18
    southern         SO      Suan Chin                    5.1     .95     4       15
    southeast        SE      Patricia Hemenway        4.0     .7       4       17
    northeast        NE      AM Main Jr.                   5.1     .94     3       13
    north              NO      Margot Weber              4.5     .89     5       9

    /> awk '$5 ~ /\.[7-9]+/' testfile     #第五個域字段匹配包含.(點),後面是7-9的數字。
    southwest       SW      Lewis Dalsass            2.7     .8      2       18
    central             CT      Ann Stephens            5.7     .94     5       13

    /> awk '$8 ~ /[0-9][0-9]$/{print $8}' testfile  #第八個域以兩個數字結束的打印。
    34
    23
    18
    15
    17
    20
    13

十.  awk表達式功能:

    1.  比較表達式:
    比較表達式匹配那些只在條件爲真時才運行的行。這些表達式利用關係運算符來比較數字和字符串。見如下awk支持的條件表達式列表:

運算符含義例子
<小於x < y
<=小於等於x <= y
==等於x == y
!=不等於x != y
>=大於等於x >= y
>大於x > y
~匹配x ~ /y/
!~不匹配x !~ /y/

    /> cat employees
    Tom Jones        4424    5/12/66         543354
    Mary Adams      5346    11/4/63         28765
    Sally Chang       1654    7/22/54         650000
    Billy Black         1683    9/23/44         336500

    /> awk '$3 == 5346' employees       #打印第三個域等於5346的行。
    Mary Adams      5346    11/4/63         28765

    /> awk '$3 > 5000 {print $1}' employees  #打印第三個域大於5000的行的第一個域字段。
    Mary

    /> awk '$2 ~ /Adam/' employess      #打印第二個域匹配Adam的行。
    Mary Adams      5346    11/4/63         28765

    2.  條件表達式:
    條件表達式使用兩個符號--問號和冒號給表達式賦值: conditional expression1 ? expression2 : expressional3,其邏輯等同於C語言中的條件表達式。其對應的if/else語句如下:
    {
        if (expression1)
            expression2
        else
            expression3
    }
    /> cat testfile
    northwest     NW        Charles Main             3.0        .98        3        34
    western        WE        Sharon Gray             5.3        .97         5        23
    southwest     SW        Lewis Dalsass           2.7        .8          2        18
    southern       SO        Suan Chin                 5.1        .95        4        15
    southeast      SE        Patricia Hemenway     4.0        .7          4        17
    eastern         EA        TB Savage                 4.4        .84        5        20
    northeast      NE        AM Main Jr.                5.1       .94         3        13
    north            NO        Margot Weber           4.5       .89         5        9
    central          CT        Ann Stephens            5.7       .94         5        13

    /> awk 'NR <= 3 {print ($7 > 4 ? "high "$7 : "low "$7) }' testfile
    low 3
    high 5
    low 2

    3.  數學表達式:
    運算可以在模式內進行,其中awk將所有的運算都視爲浮點運算,見如下列表:

運算符含義例子
+x + y
-x - y
*x * y
/x / y
%取餘x % y
^乘方x ^ y

    /> awk '/southern/{print $5 + 10}' testfile  #如果記錄包含正則表達式southern,第五個域就加10並打印。
    15.1

    /> awk '/southern/{print $8 /2 }' testfile   #如果記錄包含正則表達式southern,第八個域除以2並打印。
    7.5

    4.  邏輯表達式:
    見如下列表:

運算符含義例子
&&邏輯與a && b
||邏輯或a || b
!邏輯非!a

    /> awk '$8 > 10 && $8 < 17' testfile   #打印出第八個域的值大於10小於17的記錄。
    southern        SO      Suan Chin               5.1     .95     4       15
    central            CT      Ann Stephens         5.7     .94     5       13

    #打印第二個域等於NW,或者第一個域匹配south的行的第一、第二個域。
    /> awk '$2 == "NW" || $1 ~ /south/ {print $1,$2}' testfile
    northwest  NW
    southwest  SW
    southern    SO
    southeast   SE

    /> awk '!($8 > 13) {print $8}' testfile  #打印第八個域字段不大於13的行的第八個域。
    3
    9
    13

    5.  範圍模板:
    範圍模板匹配從第一個模板的第一次出現到第二個模板的第一次出現,第一個模板的下一次出現到第一個模板的下一次出現等等。如果第一個模板匹配而第二個模板沒有出現,awk就顯示到文件末尾的所有行。
    /> awk '/^western/,/^eastern/ {print $1}' testfile #打印以western開頭到eastern開頭的記錄的第一個域。
    western    WE
    southwest SW
    southern   SO
    southeast  SE
    eastern      EA    

    6.  賦值符號:
    #找到第三個域等於Ann的記錄,然後給該域重新賦值爲Christian,之後再打印輸出該記錄。
    /> awk '$3 == "Ann" { $3 = "Christian"; print}' testfile
    central CT Christian Stephens 5.7 .94 5 13

    /> awk '/Ann/{$8 += 12; print $8}' testfile #找到包含Ann的記錄,並將該條記錄的第八個域的值+=12,最後再打印輸出。
    25



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