linux 命令詳解 十一

   6.  數組:
    
因爲awk中數組的下標可以是數字和字母,數組的下標通常被稱爲關鍵字(key)。值和關鍵字都存儲在內部的一張針對key/value應用hash的表格裏。由於hash不是順序存儲,因此在顯示數組內容時會發現,它們並不是按照你預料的順序顯示出來的。數組和變量一樣,都是在使用時自動創建的,awk也同樣會自動判斷其存儲的是數字還是字符串。一般而言,awk中的數組用來從記錄中收集信息,可以用於計算總和、統計單詞以及跟蹤模板被匹配的次數等等。
    /> 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 '{name[x++] = $2}; END{for (i = 0; i < NR; i++) print i, name[i]}' employees    
    0 Jones
    1 Adams
    2 Chang
    3 Black
    
在上例中,數組name的下標是變量xawk初始化該變量的值爲0,在每次使用後自增1,讀取文件中的第二個域的值被依次賦值給name數組的各個元素。在END模塊中,for循環遍歷數組的值。因爲下標是關鍵字,所以它不一定從0開始,可以從任何值開始。

    #
這裏是用內置變量NR作爲數組的下標了。
    /> awk '{id[NR] = $3}; END {for (x = 1; x <= NR; x++) print id[x]}' employees
    4424
    5346
    1654
    1683

    awk
中還提供了一種special for的循環,見如下聲明:
    for (item in arrayname) {
        print arrayname[item]
    }

    /> cat db
    Tom Jones
    Mary Adams
    Sally Chang
    Billy Black
    Tom Savage
    Tom Chung
    Reggie Steel
    Tommy Tucker

    /> awk '/^Tom/{name[NR]=$1}; END {for(i = 1;i <= NR; i++) print name[i]}' db
    Tom
     Tom
    Tom
     Tommy
    
從輸出結果可以看出,只有匹配正則表達式的記錄的第一個域被賦值給數組name的指定下標元素。因爲用NR作爲下標,所以數組的下標不可能是連續的,因此在END模塊中用傳統的for循環打印時,不存在的元素就打印空字符串了。下面我們看看用special for的方式會有什麼樣的輸出。
    /> awk '/^Tom/{name[NR]=$1};END{for(i in name) print name[i]}' db
    Tom
    Tom
    Tommy
    Tom

    
下面我們看一下用字符串作爲下標的例子:(如果下標是字符串文字常量,則需要用雙引號括起來)    
    /> cat testfile2
    tom
    mary
    sean
    tom
    mary
    mary
    bob
    mary
    alex
    /> awk '/tom/{count["tom"]++}; /mary/{count["mary"]++}; END{print "There are " count["tom"] \
        " Toms and " count["mary"] " Marys in the file."} testfile2
    There are 2 Toms and 4 Marys in the file.
    
在上例中,count數組有兩個元素,下標分別爲tommary,每一個元素的初始值都是0,沒有tom被匹配的時候,count["tom"]就會加一,count["mary"]在匹配mary的時候也同樣如此。END模塊中打印出存儲在數組中的各個元素。

    /> awk '{count[$1]++}; END{for(name in count) printf "%-5s%d\n",name, count[name]}' testfile2
    mary 4
    tom  2
    alex 1
    bob  1
    sean 1
    
在上例中,awk是以記錄的域作爲數組count的下標。

    /> awk '{count[$1]++; if (count[$1] > 1) name[$1]++}; END{print "The duplicates were "; for(i in name) print i}' testfile2
    The duplicates were
    mary
    tom
    
在上例中,如count[$1]的元素值大於1的時候,也就是當名字出現多次的時候,一個新的數組name將被初始化,最後打印出那麼數組中重複出現的名字下標。

    
之前我們介紹的都是如何給數組添加新的元素,並賦予初值,現在我們需要介紹一下如何刪除數組中已經存在的元素。要完成這一功能我們需要使用內置函數delete,見如下命令:
    /> awk '{count[$1]++}; \
        END{for(name in count) {\
                if (count[name] == 1)\
                    delete count[name];\
            } \
            for (name in count) \
                print name}' testfile2
    mary
    tom
    
上例中的主要技巧來自END模塊,先是變量count數組,如果數組中某個元素的值等於1,則刪除該元素,這樣等同於刪除只出現一次的名字。最後用special for循環打印出數組中仍然存在的元素下標名稱。

    
最後我們來看一下如何使用命令行參數數組,見如下命令:
    /> awk 'BEGIN {for(i = 0; i < ARGC; i++) printf("argv[%d] is %s.\n",i,ARGV[i]); printf("The number of arguments, ARGC=%d\n",ARGC)}' testfile "Peter Pan" 12
    argv[0] is awk.
    argv[1] is testfile.
    argv[2] is Peter Pan.
    argv[3] is 12.
    The number of arguments, ARGC=4
    
從輸出結果可以看出,命令行參數數組ARGV是以0作爲起始下標的,命令行的第一個參數爲命令本身(awk),這個使用方式和C語句main函數完全一致。

    /> awk 'BEGIN{name=ARGV[2]; print "ARGV[2] is " ARGV[2]}; $1 ~ name{print $0}' testfile2 "bob"    
    ARGV[2] is bob
    bob
    awk: (FILENAME=testfile2 FNR=9) fatal: cannot open file `bob' for reading (No such file or directory)
    
先解釋一下以上命令的含義,name變量被賦值爲命令行的第三個參數,即bob,之後再在輸入文件中找到匹配該變量值的記錄,並打印出該記錄。
    在輸出的第二行報出了awk的處理錯誤信息,這主要是因爲awkbob視爲輸入文件來處理了,然而事實上這個文件並不存在,下面我們需要做進一步的處理來修正這個問題。
    /> awk 'BEGIN{name=ARGV[2]; print "ARGV[2] is " ARGV[2]; delete ARGV[2]}; $1 ~ name{print $0}' testfile2 "bob"    
    ARGV[2] is bob
    bob
    
從輸出結果中我們可以看到我們得到了我們想要的結果。需要注意的是delete函數的調用必要要在BEGIN模塊中完成,因爲這時awk還沒有開始讀取命令行參數中指定的文件。


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