Linux文本處理三劍客之awk學習筆記09:ARGC和ARGV等

簡介

ARGC和ARGV都是awk的預定義變量。

ARGC存儲了awk的CLI參數數量(Argument Count);ARGV(Argument Value)是一個數組變量,雖然是關聯數組不過其下標是從0開始的數值(當然,內部識別爲字符串),存儲了這次awk的CLI執行中的每個參數。

# awk -va=1 -F: 'BEGIN{print ARGC;for(i in ARGV){print i,ARGV[i]}}' b=3 a.txt b.txt
4
0 awk
1 b=3
2 a.txt
3 b.txt

通過該示例我們可以發現。數組的參數包含了awk命令本身(且它是數組下標爲0的第一個參數),選項型參數和非選項型參數(這塊不懂請參見這裏)。

ARGV[0] ==> "awk"
ARGV[1] ==> "b=3"
ARGV[2] ==> "a.txt"
ARGV[3] ==> "b.txt"

因此ARGC的值就爲4,即ARGC也是ARGV的數組長度(length(ARGV))。ARGV不包含選項以及具體的awk代碼。

-va=1
-F:
'BEGIN{print ARGC;for(i in ARGV){print i,ARGV[i]}}'

ARGV的元素數量/長度只有在awk執行的開始時等於ARGC,在代碼執行的過程中我們可以對這兩個預定義變量進行修改。

ARGV和ARGC的內容決定了本次awk執行過程中如何對待參數,僞代碼形似如下:

for(i=1;i<ARGC;i++){
    ... process ARGV[i] ...
}

只減小ARGC的值會導致原本應該被讀取到的參數丟失,即可能出現尾部文件無法被讀取的情況。

awk '{print}' a.txt b.txt
awk 'BEGIN{ARGC--}{print}' a.txt b.txt

只增加ARGC的值不會對結果產生任何影響。

awk 'BEGIN{ARGC++}{print}' a.txt b.txt

如果我們對ARGV中的元素進行新增或者刪除,那麼需要同時改變ARGC的值纔會使得結果是我們所期待的。

awk 'BEGIN{ARGV[ARGC]="c.txt"}{print}' a.txt b.txt
awk 'BEGIN{ARGV[ARGC++]="c.txt"}{print}' a.txt b.txt

在這裏,3個文件,原本ARGC的值應該是4,我們手動設定爲了3,因此只會讀取a.txt和b.txt。

awk 'BEGIN{ARGC=3}{print}' a.txt b.txt c.txt

如果我們將ARGV[1](即a.txt)刪除或者置空,那麼awk依然只會讀取2個參數(不包括awk命令本身),第一個參數是空字符串,第二個參數是b.txt。並不會因爲少掉了一個參數,該位置就被後續的給頂替了。c.txt是一直無法被讀取的。也就是說空字符串參數也可以被讀取。

awk 'BEGIN{ARGC=3;delete ARGV[1]}{print}' a.txt b.txt c.txt
awk 'BEGIN{ARGC=3;ARGV[1]=""}{print}' a.txt b.txt c.txt
# awk 'BEGIN{print ARGC}' a.txt b.txt c.txt ""
5

另外再來看幾個預定義變量。

FILENAME:顧名思義,文件名,存儲當前正在處理的文件的文件名。

awk 'FNR==1{print FILENAME}' a.txt b.txt c.txt

ARGIND:參數的索引值(Argument Index)。存儲你當前正在處理的文件在ARGV中的索引值。因此,當awk正在處理的參數剛好是文件的時候,“FILENAME==ARGV[ARGIND]”總是會返回true。

ENVIRON:這是一個數組變量,保存了shell的環境變量。

ENVIRON["SHELL_ENV"]    # 這裏的SHELL_ENV要替換成shell下具體的環境變量。
# echo $SHELL
/bin/bash
# awk 'BEGIN{print ENVIRON["SHELL"]}'
/bin/bash
# echo $HOME
/root
# awk 'BEGIN{print ENVIRON["HOME"]}'
/root

 

ARGC和ARGV的妙用

當我們使用awk處理文件時,如果文件的權限不足,或者文件不存在,那麼awk就會報錯並且退出,不再處理報錯文件的後續文件。

注意:我這裏使用了普通用戶alongdidi來測試,root用戶權限較大,即使權限000依然可讀取。

$ ls -l {a,aa,aaa}.txt
ls: cannot access aaa.txt: No such file or directory
---------- 1 alongdidi alongdidi   0 Jan 26 16:44 aa.txt
-rw-rw-r-- 1 alongdidi alongdidi 566 Jan 26 16:47 a.txt
$ awk '{print}' {a,aa,aaa}.txt
ID  name    gender  age  email          phone
... ...
awk: cmd. line:1: fatal: cannot open file `aa.txt' for reading (Permission denied)
# 到這裏就結束了,並不會處理aaa.txt
$ awk '{print}' {a,aaa}.txt
ID  name    gender  age  email          phone
... ...
awk: cmd. line:1: fatal: cannot open file `aaa.txt' for reading (No such file or directory)

我們可以使用ARGC和ARGV來將那些無法正常讀取的文件從待處理的文件中剔除。

思路如下:

  • 必須在main之前就修改待處理的文件,因此代碼應寫在BEGIN中。
  • 結合getline的返回值特性來判斷文件的可讀性,getline正常的文件要記得關閉。
  • 參數可能會是選項型參數(變量賦值var=val)或者標準輸入(-和/dev/stdin),這類情況需要排除。
  • 由於是刪除ARGV的元素,因此不要減小ARGC的值不然可能會造成位於尾部的正常文件無法被處理。

 

$ cat ARGCandARGV.awk 
BEGIN{
    for(i=1;i<ARGC;i++){
        if(ARGV[i]~/[[:alpha:]_][[:alnum:]_]*=.*/ || ARGV[i]=="-" || ARGV[i]=="/dev/stdin"){
            continue
        }else if((getline var < ARGV[i])<0){
            delete ARGV[i]
        }else{
            close(ARGV[i])
        }
    }
    for(i=1;i<ARGC;i++){
        print i,ARGV[i]
    }
}

由普通用戶alongdidi執行看結果。

$ awk -f ARGCandARGV.awk a=3 {a,aa,aaa}.txt - /dev/stdin
1 a=3
2 a.txt
3 
4 
5 -
6 /dev/stdin

變量賦值、標準輸入被跳過,保留在ARGV中。可正常讀取的文件a.txt被保留,無權限讀取(aa.txt)和不存在的文件(aaa.txt)被移出ARGV。

因此此可再次執行代碼就不會報錯了。用戶也可以把main代碼寫在-f指定的文件中,我直接使用-e指定了。

$ awk -f ARGCandARGV.awk -e '{print}' a=3 {a,aa,aaa}.txt - /dev/stdin
1 a=3
2 a.txt
3 
4 
5 -
6 /dev/stdin
ID  name    gender  age  email          phone    # 由於我們在代碼中有正確使用close(),因此文件在打印時依然是從首行開始。
... ...

 

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