麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 操作系統 > 正文

GNU awk命令用法介紹

2024-06-28 16:04:48
字體:
來源:轉載
供稿:網友

AWK簡介

AWK是一門解釋型的編程語言,它的名字來源于它的三位作者的姓氏:Alfred Aho,Peter Weinberger和Brian Kernighan。AWK能夠應用于廣泛的計算和數據處理任務。所有的GNU/linux發行版都自帶GAWK,即GNU AWK,是AWK的擴展并且與AWK完全兼容。

和上面講到的sed命令類似,AWK逐行讀取輸入流中的內容,并對讀取的行執行所有命令,如此循環,直到輸入流結束。

本文基于GAWK進行介紹,因為GAWK比原生AWK使用更普遍些。

程序結構

AWK命令格式如下:

awk [options] 'PRogram' input-file1 input-file2 ...

或者

awk [options] -f program-file input-file1 input-file2 ...

第一種格式中,awk從file中獲取輸入流,然后執行單引號內的程序。第二種格式則是從文件program-file中獲取將要執行的程序。

上述AWK命令的program部分的結構可分為三塊:BEGIN、BODY和END。

BEGIN:在AWK命令的一開始執行的動作,它只執行一次,可以把變量初始化放在這里。注意,BEGIN部分是可選的,并且一個AWK命令中可以有多個BEGIN塊。注意,如果有-v選項的賦值操作,則-v的操作在BEGIN之前。

BEGIN塊的寫法為:BEGIN{…}

BODY:程序主體,對輸入流的每一行執行動作。如果存在BEGIN或END,則這部分是可選的。一個AWK命令中可以有多個BODY塊。

BODY塊的寫法為:{…}

END:在AWK命令的最后執行的動作,它只執行一次。注意,END部分是可選的,并且一個AWK命令中可以有多個END塊。注意,使用END { }塊時,awk的file參數不能省略。

END塊的寫法為:END{…}

例如下面的命令:

[root@Ubuntu]awk_test:$ awk 'BEGIN{print "Output start!"}; {print}; END{print "Output done!"}' awk_test.txt Output start!USER       PID %MEM    VSZ   rss STAT START   TIME COMMANDroot         1  0.1   3652  1916 Ss   Jan07   0:03 /sbin/initroot         2  0.0      0     0 S    Jan07   0:00 [kthreadd]root         3  0.0      0     0 S    Jan07   0:02 [ksoftirqd/0]root        26  0.0      0     0 S    Jan07   0:40 [kswapd0]user       495  0.1   3588  1092 Ss   Jan07   0:00 /sbin/udevd --daemonuser       860  0.0   3584   908 S    Jan07   0:01 /sbin/udevd --daemonuser      1137  0.0   4520   776 S    Jan07   0:00 smbd -Fuser      1550  0.1   4521  1816 Ss   Jan07   0:15 nmbd -DOutput done!

可以看到這條命令在命令的開始打印一行輸出“Output start!”,然后對文件中的每一行內容執行print操作,最后又打印出一行輸出“Output done!”。

從句法結構上來講,program由一條條規則組成,每條規則由模式動作組成,即模式匹配后執行相應的動作。動作放在花括號內以與模式區分。所以,program一般的格式是這樣的:

pattern { action }

pattern { action }

那么,下面這條命令(打印長度大于80字符的行):

awk 'length($0) > 80' data

可以看到這條awk命令只有pattern而沒有action部分。如果沒有action部分,則執行默認動作:打印整個record。

也可以把program寫到文件里(不用加單引號),通過AWK的第二種格式來執行。
[root@ubuntu]awk_test:$ cat progfile BEGIN{print "Output start!"};{print};END{print "Output done!"};[root@ubuntu]awk_test:$ awk –f progfile awk_test.txt

為了方便后期維護,建議將AWK的程序文件以.awk作為后綴名。

另外,可以利用Shell的#!機制,將progfile內容改為:
#!/usr/bin/awk –fBEGIN{print "Output start!"};{print};END{print "Output done!"};

這樣的話,執行./progfile awk_test.txt即可。(通過type awk可以知道系統中awk命令的位置。)

注:使用#!機制的話,執行的shell命令./progfile實際上是執行:"#!后面的命令" +"./progfile腳本" + "./progfile腳本的參數"。另外,這種寫法,awk后面最多只能跟一個參數;并且ARGV[0]的值在不同系統上可能表現不同,比如可能被解釋為awk或/usr/bin/awk或./progfile。

比較特別的,如果要在program內使用或打印單引號,可以用其ASCII碼'/47'表示。或者把程序寫在文件中,這樣就不用擔心單引號和program外圍的單引號混淆的問題。注意,在單引號中,反斜杠后接一個字符,會被解釋成這個字符的字面意思,即和不加反斜杠是一樣的含義。

你也可以通過下面兩種方法來打印單引號:

awk 'BEGIN { print "Here is a single quote <'"'"'>" }'

awk 'BEGIN { print "Here is a single quote<'/''>" }'

AWK選項

 -f program-file 執行文件中的程序,前面已講過。

Program-file可以有多個,通常用來將通用的代碼或函數做成庫,以實現代碼復用。

環境變量AWKPATH用來指定-f的搜索路徑,如果不指定AWKPATH,則默認搜索“.:/usr/local/share/awk”,可以通過修改AWKPATH或ENVIRON["AWKPATH"]來修改搜索路徑,每個路徑之間用冒號隔開(.::都可以表示當前路徑)。如果-f選項后面跟的是包含“/”的文件名,就不會去額外搜索路徑了。

 -v var=value 變量賦值,在BEGIN之前進行。

例如,定義變量name,并賦值為“jason”:

[root@ubuntu]awk_test:$ awk -v name=jason 'BEGIN{printf("name=%s/n", name)}'name=jason

注意變量的引用不需要加“$”,實際上AWK的語法很多跟ANSI C的語法類似。“$”在AWK中是用來引用field的,后面會講到。

 -F fs 使用fs作為分隔符(默認是空格)

例如,打印/etc/passwd文件中的用戶名一列:
[root@ubuntu]awk_test:$ cat /etc/passwd | awk -F ':' '{print $1}'rootdaemonbinsyssyncgames

 --compat 或 --traditional 使用原生awk,不會識別gawk的擴展。例如,原生awk允許在while/for/do循環外面使用continue和break語句,這會被認為和next語句意思相同。Gawk添加--traditional則可以使用這一特性。 --dump-variables[=file] 輸出排好序的AWK內置的全局變量到文件(若不指定文件,則默認為awkvars.out)

這個選項可以查看當前可用的內置全局變量。在編程的時候要注意,不要定義和這些內置變量重名的變量。

 --non-decimal-data 識別輸入流中的十六進制和八進制

例如,將下面文件中的數字相加求和:
[root@ubuntu]awk_test:$ cat number.txt 0x120x3201210[root@ubuntu]awk_test:$ awk '{sum+=($1)}; END{print sum}' number.txt 22[root@ubuntu]awk_test:$ awk --non-decimal-data '{sum+=($1)}; END{print sum}' number.txt 88

可以看到,如果不加--non-decimal-data選項,就只識別出十進制的12和10。

不過man gawk中說“Use this option with great caution!”,一方面這個選項可能破壞舊的程序,另一方面這個選項可能在以后被摒棄。

 --profile[=prof_file] 生成awk命令的profile文件

這個選項以優雅的格式將awk命令保存到文件,如果不指定文件名,則默認為awkprof.out。
[root@ubuntu]awk_test:$ awk --non-decimal-data --profile '{sum+=($1)}; END{print sum}' number.txt[root@ubuntu]awk_test:$ cat awkprof.out 	# gawk profile, created Sun Jan  8 23:20:48 2017	# Rule(s)	{		sum += $1	}	# END block(s)	END {		print sum	}

如果使用pgawk執行命令,則還會顯示每條語句以及每個函數的調用次數。

 --re-interval 在正則表達式中支持間隔表達式

傳統的awk不支持間隔表達式,必須加上--re-interval選項或者--posix選項才能使用。

 -e program-text --source program-text  program-text為awk的program源碼,這個選項允許將文件中的源碼和命令行中的源碼混合使用。在需要引用自定義的庫函數時就可以使用該選項,例如:

awk -f func_test.awk --source 'BEGIN{printadd_INT(1,2)}' awk_test.txt

這個例子中,func_test.awk里面定義了函數add_INT(),這里將-f指定的文件程序和--source指定的命令行程序混合在了一起。

 -E file--exec file  意義和-f選項相同,不過有兩點區別:命令行中的其他選項都直接傳給awk,而awk先處理其他選項和參數,最后才處理--exec選項;另外,不允許“var=value”形式的變量賦值。

這個選項應該在#!開頭的腳本里使用,例如:

#! /usr/local/bin/gawk -E awk program here …

這個選項可以防止向腳本里傳遞參數,因為所有的參數都先被awk識別并處理了。

--include source-file--load ext

這兩個選項都是針對引用函數庫的,也可以在文件中使用@include和@load來引用庫文件。不過在我所用系統的gawk不支持這兩個參數以及相應的AWKLIBPATH環境變量,我就不介紹了。我們就使用-f來引用庫文件吧,只是-f的文件里面可以是任何程序內容,并不是只針對庫文件而設計的。

 -- 標記選項的結束

這告訴awk選項部分已經結束,可以用來傳遞以“-”開頭的參數,而不被誤認為是選項。

AWK的變量、Records和Fields

AWK的變量是動態生成的,在第一次被使用的時候開始存在。變量的值可以是下列數據類型:浮點型字符串類型一維數組。甚至一個變量既可以是浮點型也可以是字符串類型,這取決于代碼中如何使用它。

AWK會將“var=value”形式的參數認為是變量賦值,例如下面這個命令,awk將“var=2”和“var=1”認為是給var賦值,而不是一個文件名,這個過程在awk順序處理參數列表時進行的。

awk 'var == 1 {print 1} var == 2 { print 2}'  var=2 awk_test2.txt var=1 awk_test1.txt

Records

一個record就是awk認為的一行輸入,對一個輸入流,默認以換行符分隔。不過可以通過內置變量RS來修改。例如,把RS賦值為Jan07:

[root@ubuntu]awk_test:$ awk -v RS=Jan07 '{print}' awk_test.txt USER       PID %MEM    VSZ   RSS STAT START   TIME COMMANDroot         1  0.1   3652  1916 Ss      0:03 /sbin/initroot         2  0.0      0     0 S       0:00 [kthreadd]root         3  0.0      0     0 S       0:02 [ksoftirqd/0]root        26  0.0      0     0 S       0:40 [kswapd0]user       495  0.1   3588  1092 Ss      0:00 /sbin/udevd --daemonuser       860  0.0   3584   908 S       0:01 /sbin/udevd --daemonuser      1137  0.0   4520   776 S       0:00 smbd -Fuser      1550  0.1   4521  1816 Ss      0:15 nmbd -D

看效果就知道是什么意思了。注意“Jan07”本身沒有打印出來。

如果RS被賦值為單個字符,則這個字符就是records的分隔符;如果被賦值為多個字符,那RS實際上是一個正則表達式。

如果RS被賦值為空,則以空行作為record的分隔符。

fields

AWK會把一個record再分隔成一個個的field依次進行處理,以變量FS的值作為分隔符。

如果FS被賦值為單個字符,則這個字符就是fields的分隔符;如果被賦值為多個字符,那就是一個正則表達式;如果FS是空,則record中的每個字符都是分隔符。

注意,如果FS是空格,則多個空格、tab和換行,都會被認為是分隔符。

一個record中的各個field可以通過各自的位置來引用,依次為$1$2$3,...。而$0表示整個record。

如果給FIELDWIDTHS變量賦值為一個數值列表(以空格隔開),如下面的例子,record就會按照字符串長度來分隔fields而不是按照FS的值。這時FS變量會被忽略。

[root@ubuntu]awk_test:$ awk 'BEGIN{FIELDWIDTHS="2 4 4 4"}{print $1"||"$2"||"$3"||"$4}' awk_test.txt US||ER  ||    || PIDro||ot  ||    ||   1ro||ot  ||    ||   2ro||ot  ||    ||   3ro||ot  ||    ||  26us||er  ||    || 495us||er  ||    || 860us||er  ||    ||1137us||er  ||    ||1550

如果給FS重新賦值,就會切回到按照FS分隔fields。

NF變量用于獲取當前record共分了多少了fields。你可以給NF、$0以及某個field賦值,那么相應record會更新并重新劃分fields。例如:
[root@ubuntu]awk_test:$ awk '{NF-=1; if (FNR!=1) $1="json"; print}' awk_test.txt USER PID %MEM VSZ RSS STAT START TIMEjson 1 0.1 3652 1916 Ss Jan07 0:03json 2 0.0 0 0 S Jan07 0:00json 3 0.0 0 0 S Jan07 0:02json 26 0.0 0 0 S Jan07 0:40json 495 0.1 3588 1092 Ss Jan07 0:00 /sbin/udevdjson 860 0.0 3584 908 S Jan07 0:01 /sbin/udevdjson 1137 0.0 4520 776 S Jan07 0:00 smbdjson 1550 0.1 4521 1816 Ss Jan07 0:15 nmbd這個例子中,把NF減1,則原來的每個record的最后一個field就沒有打印出來。FNR表示當前已經輸入了幾個record,例子中將文件除了第一行之外的第一個field的值都改成了“jason”。

內置變量

AWK有一些內置的全局變量可以直接使用,會使編程更方便。

內置變量

含義

ARGC

Awk命令行的參數個數(不包括選項和選項的值以及program部分),awk自身是第0個參數。

ARGV

Awk的參數列表,共ARGC個元素。

ARGIND

當前正在處理的文件處于ARGV數組中的index。

BINMODE

是否使用binmode打開文件。1("r"),2("w"),3("rw")分別表示輸入文件、輸出文件、所有文件需要使用binmode打開。

CONVFMT

數字的格式化類型,默認是"%.6g"。

ENVIRON

保存當前所有環境變量的數組。這個數組是以環境變量名作為下標的,例如ENVIRON["HOME"]的值是"/root"。

ERRNO

當getline或close失敗,ERRNO會保存字符串形式的錯誤描述。

FIELDWIDTHS

一個以空格分隔的數值列表,設置這個值后,FS失效,record改以字符長度來劃分fields。

FILENAME

Awk當前處理的文件的名字。注意,在BEGIN{ }中由于還沒開始處理文件,FILENAME是空(除非被getline設置)。

FNR

表示正在處理的文件目前已經輸入了幾個record。

FS

Field分隔符,默認是空格。

IGNORECASE

是否忽略大小寫,默認是0。用于控制正則表達式和字符串操作,例如會影響records和fields的分隔符。注意,數組下標不受影響。

LINT

如果為true,則對可能不兼容的awk命令提示warning,如果為fatal,則提示為錯誤。默認是0:不提示。

NF

當前record中的fields的數目。

NR

已經處理的record的數目(包括當前record)。

OFMT

輸出中數字的格式,默認是“%.6g”。

OFS

輸出中fields的分隔符,默認是空格。例如下面的命令,就會以” : ”來打印fields(注意$1 $2 $3之間要加逗號):

awk -v OFS=" : " '{print $1,$2,$3}' awk_test.txt

ORS

輸出中records的分隔符,默認是新行。

PROCINFO

一個包含正在執行的awk命令自身信息的數組,以元素名作為下標,例如PROCINFO[“egid”],PROCINFO[“pid”]等,下面的命令可以列舉所有的元素:

awk '{for (var in PROCINFO) print var;}' awk_test.txt

RS

records分隔符,默認是新行。

RT

record的結束符,通常被賦值為RS。

RSTART

match()匹配成功的子串的第一個字符在原始字符串中的index(從1開始)。0表示匹配失敗。

RLENGTH

match()匹配成功的子串的長度。-1表示匹配失敗(可以匹配空串,此時長度為0)。

SUBSEP

數組下標分隔符,默認為/034即0x1C的ASCII符號。在訪問多維數組時有用。

TEXTDOMAIN

文本域,本地化的時候用到。

對于FS,強調一個‘FS =" "’和‘FS = "[ /t/n]+"’的不同,這兩種FS都是將多個空格、tab或newline作為分隔符。但是前者會先將record中的前導空格和尾部空格剝掉,再決定如何分割fields。例如下面兩條命令的結果就不同:

[root@ubuntu]awk_test:$ echo ' a b c d ' | awk '{ print $2 }'b[root@ubuntu]awk_test:$ echo ' a b c d ' | awk 'BEGIN { FS = "[ /t/n]+" }{ print $2 }'A也可以據此特征來刪除前導空格:
[root@ubuntu]awk_test:$ echo '   a b c d' | awk '{ print; $2 = $2; print }'   a b c da b c d

這條命令通過$2=$2,讓awk重新劃分fields,由于FS模式是空格,這時就會先把前導空格和尾部空格剝掉。

數組

AWK中的數組是關聯數組(鍵值對的形式),數組下標放在方括號“[ ]”內,數組下標可以是數值或字符串。可以用(expr, expr ...)來模擬多維數組的下標。例如:

i = "A"; j = "B"; k = "C"x[i, j, k] = "hello, world/n"

上面定義了數組x下標為"A/034B/034C"的值為"hello,world/n"。

操作符“in”可以用來測試下標是否存在,例如:

if (val in array)print array[val]

如果是多維的下標,則寫成 if ((i, j) in array) print array[i, j]

也可以用“in”來遍歷數組:

下面兩個例子,前者定義了一個一維數組,數組下標是[“a,b”]的元素。后者用來定義多維數組,例子中定義了三維數組中下標為[”a”][“,”][”b”]的元素。
[root@ubuntu]awk_test:$ awk 'BEGIN{array["a"",""b"]=1;for(i in array) print i}'a,b[root@ubuntu]awk_test:$ awk 'BEGIN{array["a",",","b"]=1;for(i in array) print i}'a/034,/034b

記住,數組的小標總是字符串,如果以數值為下標,也會轉換成字符串,也就是說,a[17]的下標是"17",并且和a[021]、a[0x11]是相同的下標。

使用delete可以刪除一個數組元素,例如delete array[1],也可以delete array來刪除整個數組。

變量類型轉換

變量和fields可以是字符串或浮點數類型,一個變量和field的值是什么類型依賴于它處于的上下文,例如,如果用于數學表達式,就被當為數值類型。

如果需要強制變量或field被當作數值,則寫成var+0。如果需要強制被當作字符串,則連接一個空串,例如var ""

雖然所有的數值都是浮點型的,但是對于一個整數,轉換成字符串的結果就是整型,例如12會被轉換為“12”而不是“12.00”。

在比較兩個變量var1和var2時,如果兩個變量都是浮點型,則按照數值來比較;如果一個浮點型,另一個是字符串,則按照數值來比較,這時,字符串變量被當做“數值字符串”,例如("12"==12)的值為1;如果兩個變量都是字符串,則按照字符串來比較。

注意,只有在處理用戶輸入時,才會將形如“57”這種看起來像數值的字符串認為是“數值字符串”,例如getline的輸入、FILENAME、ARGV的成員、ENVIRON的成員以及split()產生的數組元素。其他情況下,則只是一個字符串常量。

未初始化的變量,會有兩個默認的初始值:數值0以及空字符串""。

八進制和十六進制

gawk識別八進制和十六進制,例如011、0x16。

字符串常量

字符串常量是指雙引號內的字符序列。在字符串中,可以包含轉義字符,如下:

轉義字符

含義

//

字面含義的反斜杠

/a

“alert”字符,通常是ASCII中的BEL

/b

backspace,回車

/f

form-feed,換頁符

/n

newline,換行符

/r

carriage return,回車

/t

horizontal tab

/v

vertical tab

/xhex

十六進制數表示的ASCII碼字符。例如/x2A表示ASCII中的“*”號

/ddd

一個、兩個或三個數字組成的八進制數值表示的ASCII碼字符。例如/052表示ASCII中的“*”號,注意/52同/052相同

/c

字面含義的字符c,例如需要使用原意的*號,則需要寫成/*。

轉義字符也可以用在正則表達式中,例如/[ /t/f/n/r/v]/表示匹配空白字符,/a/52b/同/a/*b/。

模式和動作

AWK是一個line-oriented的語言,對每一行(record),先進行模式匹配,再執行動作。動作的語句放在花括號{ }里面。如果模式為空,則對所有record執行動作,例如 { print } ,就是無條件的打印整個record。

用“#”對一行程序進行注釋,直到換行則注釋內容結束。

正常情況下,一個完整的語句以換行結束,但一個語句也可以寫成多行,如果一個語句在“,”、“{”、“?”、“:”、“&&”“||”處換行,則認為該語句尚未完成;一個語句在一行中以do或else結尾,也會認為該語句未完成而繼續讀取下一行;其他情況下,可以在行末添加“/”來標記這一行尚未結束(建議不要在pattern或字符串以及注釋的中間用反斜杠換行)。

也可以將多個語句寫到一行中,語句之間用分號“;”隔開。

模式(patterns)

AWK的模式可以是下面的其中一種:

  BEGIN  END  /regular expression/  relational expression  pattern && pattern  pattern || pattern  pattern ? pattern : pattern  (pattern)  ! pattern  pattern1, pattern2

BEGINEND是兩個比較特殊的模式,他們不對輸入做模式匹配,因為BEGIN和END分別是在處理輸入之前和之后。多個BEGIN塊會被merge成一個BEGIN塊,這個BEGIN里面的語句會在開始讀取輸入之前執行;多個END塊會被merge成一個END塊,這個END里面的語句會在處理完所有輸入之后執行(或者執行exit)。注意, BEGIN和END必須是獨立的,不能糅合在其他pattern表達式中。并且BEGIN和END不能沒有action部分,也就是說BEGIN和END后面不能沒有花括號{ }。

對于/regular expression/模式,會對所有匹配到該正則表達式的record執行其關聯的語句。例如,打印包含“root”的行:

awk '/root/ {print $0}' awk_test.txt

一個relationalexpression可以使用后面將要講到的任何運算符,這種模式通常用來測試一個field是否匹配某個正則表達式。關系表達式是指使用關系運算符(>,>=,<,<=,==,!=)連接一個或兩個表達式組成的式子。

“&&”、“||”和“!”即我們熟知的與、或、非邏輯運算符,可以用來將多個patterns連接在一起,這時他們是short-circuite valuation(短路求值)的,例如對于&&操作,只要第一個值是false,那整個表達式就是false,就沒必要計算后續的值了。圓括號( )可以改變運算符的運算順序。

例如,打印第一個field不是“root”的行且第三個field等于“0.0”的行:
awk '$1 != "root" && $3 == "0.0" {print}' awk_test.txt

“?:”運算符和C語言中的三目的條件運算符一樣,pattern1為true則選用pattern2,否則選用pattern3。

pattern1, pattern2形式的表達式被稱為范圍樣式,它將匹配pattern1的record,匹配pattern2的record以及這兩個record之間的所有record都匹配出來。

正則表達式

正則表達式由下表中一系列的字符集合組成。注:其中c表示character,r表示regular expression。

字符

含義

c

匹配非元字符c

/c

匹配元字符c的字面含義。由于元字符有特殊含義,因此必須加反斜杠來匹配其原始的字符含義。這些字符包括"["、"]"、"/"、"^"、"$"、"."、"|"、"?"、"*"、"+"、"{"、"}"、"("、")"等。

.

匹配一個字符(包括換行)

^

匹配字符串的開頭

$

匹配字符串的結尾(我系統上不支持)

[abc...]

匹配字符列表abc…中的任何一個字符

[^abc...]

匹配字符列表abc…之外的任何一個字符

r1|r2

匹配r1或r2

r1r2

匹配r1且其后緊跟r2

r+

匹配一個或多個r

r*

匹配0個或多個r

r?

匹配0個或1個r

(r)

分組,匹配圓括號中的所有字符組合r

r{n}

r{n,}

r{n,m}

花括號內有1個或2個數值被稱為區間表達式:只有一個數字n,則表示前面的正則表達式r重復n次;有一個數字n和一個逗號,表示r至少重復n次;有兩個數字n和m,表示r重復n到m次。

區間表達式只有在指定--posix或--re-interval的時候才可用。

/y

匹配一個單詞開頭和結尾的空串,例如‘/yballs?/y’匹配‘ball’或‘balls’,但不匹配‘football’

/B

和/y相反

/s

匹配空白字符,同[[:space:]]

/S

匹配非空白字符,同[^[:space:]]

/<

匹配一個單詞開頭的空串,例如//<away/匹配‘away’但不匹配‘stowaway’

/>

匹配一個單詞結尾的空串

/w

匹配Word-constituent的字符(包括字母、數字和下劃線),同‘[[:alnum:]_]’

/W

匹配非word-constituent的字符,同‘[^[:alnum:]_]’

/`

匹配一個buffer(字符串)開頭的空串,同^

/'

匹配一個buffer結尾的空串,同$

上表中紅色標記的字符用于連接多個正則表達式,我們稱之為“正則表達式運算符”或“元字符”。

在字符串常量中有效的轉義字符,在正則表達式中同樣有效。

正則表達式通常用兩個斜杠“/ /”包裹起來作為pattern使用,我們稱之為正則表達式常量。

在中括號表達式中引用‘/’、‘] ’、‘-’或‘^’,需要加反斜杠,例如“[d/]]”匹配字符'd'或字符']'。

正則表達式的匹配遵循最左開始最長的匹配的原則,例如下面表達式的結果是"<A>bcd":

echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'

字符類是POSIX標準中引入的特性,一個字符類表示了某個特定屬性的字符的集合。POSIX標準定義的字符類包括:

字符類

含義

[:alnum:]

字母或數字

[:alpha:]

字母

[:blank:]

空格或tab

[:cntrl:]

控制字符

[:digit:]

數字

[:graph:]

可打印且可見的字符(例如space可打印但不可見,而字符a既可打印又可見)

[:lower:]

小寫字母

[:print:]

可打印的字符(非控制字符)

[:punct:]

標點符號(非字母、數字、控制字符和空白字符)

[:space:]

空白字符(例如空格、tab、換頁符等等)

[:upper:]

大寫字母

[:xdigit:]

十六進制數字

字符類在使用時必須放在正則表達式的方括號[ ]里面,例如要匹配包含字母’n’或者數字的record,可以這樣寫:

[root@ubuntu]awk_test:$ awk '/[n[:digit:]]/' regexp.txt

我們有時將匹配字母和數字寫成/[A-Za-z0-9]/,但在本地化的時候,一些國家的字母表的字符集并不是這些字符,這時我們使用/[[:alnum:]]/就可以準確的匹配而不必擔心本地化后的差異。

還有兩個特殊的字符序列:Collating Symbols和Equivalence Classes,應用在非英語國家的本地化,例如用一個字符來表示多個字符/[[.ch.]]/或者定義多個等價字符/[[=e=]]/,有興趣的可以自己查閱相關資料。

注意,/y,/B,/s,/S,/<,/>,/w,/W,/`和/'這些正則表達式的運算符是gawk特有的,gawk的不同選項可能影響如何解析正則表達式:

無選項:上述所有運算符均有效(區間表達式除外)。

--posix:只支持POSIX標準(包括區間表達式)的,那么/w就只表示字面意思的'w'。

--traditional:只支持傳統的UNIX awk的正則表達式,此時,GNU的運算符、區間表達式、字符類都不支持,八進制和十六進制的轉義字符("/ddd","/xhex")也會按字面意思解析。

--re-interval:支持區間表達式(即使有--traditional選項)。

我們可以通過將IGNORECASE設置為一個非0的值,使AWK在匹配時忽略大小寫。也可以用toupper()/tolower()或中括號表達式,來只對某一些規則忽略大小寫。

動作(actions)

AWK中的動作語句(action statements)都放在花括號{ }中,由大部分語言都有的賦值、條件和循環語句組成。AWK中的運算符、控制語句以及輸入輸出語句都是仿照C語言來定義的。

運算符

AWK中運算符按照優先級由高到低,列舉如下:

運算符

含義

(…)

分組

$

field引用

++  --

自增、自減,均可前置和后置

^

冪運算(也可以用**,對應的冪運算賦值**=)

+  -  !

一元運算符的+、-、邏輯非

*  /  %

乘、除、取模

+  -

加、減

space

字符串連接

|  |&

管道IO、協同進程的管道IO。getline,print和printf使用

<  >  <=  >=  

!=  ==

關系運算符

~  !~

正則表達式的匹配、不匹配,一般用法是/exp/ ~ /regexp/,其中右側可以是任何表達式(不需要必須是正則表達式常量)。例如$0 ~ /foo/意為查找匹配foo的$0,若匹配,則返回1,不匹配返回0。

in

獲取數組成員

&&

邏輯與

||

邏輯或

?:

條件表達式,格式為expr1 ? expr2 : expr3,如果expr1為true,則表達式的值為expr2,否則為expr3

=  +=  -=  *= 

/=  %=  ^=

賦值運算符,均支持a=a+b和a+=b這兩種形式。

控制語句

控制語句列舉如下:

  if (condition) statement [ elsestatement ]  while (condition) statement  do statement while (condition)  for (initialization; condition; increment) statement  for (var in array) statement  switch (expression) {case value or regular expression:   case-bodydefault:   default-body}  break  continue  delete array[index]  delete array  exit [ expression ]  { statements }

這些控制語句和ANSI C中類似語句的語法相同。Switch的每個case分支通常要添加break語句以保證唯一匹配。

I/O語句

輸入輸出語句列舉如下:

               I/O statement                                                      

解釋

close(file [, how])

關閉文件、管道或協同進程。參數“how”只有在關閉協同進程的雙向管道其中一端時才會用到,是字符串類型,可取"to"或"from"。

getline

把$0賦值為下一個輸入的record。同時會更新NF、NR和FNR。

getline <file

把$0賦值為文件file的下一個record。同時會更新NF。

getline var

把變量var賦值為下一個輸入的record。同時會更新NR和FNR。

getline var <file

把變量var賦值為文件file的下一個record。

command | getline [var]

執行command并將輸出作為getline或getline var的輸入。

command |& getline [var]

執行協同進程command并將輸出作為getline或getline var的輸入。(協同進程是gawk的擴展,command也可以是一個socket)

next

停止處理當前的record,并讀取下一個record,然后重新從awk程序中第一個pattern開始處理新的record。如果當前已經達到輸入數據中最后一個record,則開始執行END{ }程序塊。

nextfile

停止處理當前的文件,并從下一個文件中讀取record,然后重新從awk程序中第一個pattern開始處理新的record。FILENAME和ARGIND參數會被更新,FNR被重置為1。如果當前已經達到輸入數據中最后一個record,則開始執行END{ }程序塊。

print

打印當前的record(根據ORS的值判斷record是否輸出完畢)。

print expr-list

打印表達式列表。如果有多個表達式,每個表達式之間用OFS分隔。(根據ORS的值判斷record是否輸出完畢)

print expr-list >file

打印表達式列表到文件。如果有多個表達式,每個表達式之間用OFS分隔。(根據ORS的值判斷record是否輸出完畢)

printf fmt, expr-list

格式化的打印

printf fmt, expr-list >file

格式化的打印到文件。例如:

awk 'BEGIN{printf "%#x", 10 >"getnum.txt"}'

system(cmd-line)

執行命令cmd-line,并將exit status作為返回值。例如:

system("uname –a")

fflush([file])

清空已打開的輸出文件或管道文件的所有緩存。如果不帶file參數,則清空標準輸出,如果file參數為null string,則所有打開的輸出文件和管道的緩存都會被清空。

另外,print和printf也允許以下形式的輸出重定向:

print ... >> file

將輸出追加到文件file

print ... | command

向管道寫內容

print ... |& command

向協同進程或socket發送數據

getline命令執行成功返回1,到達文件結尾返回0,出錯返回-1。如果出錯,則字符串ERRNO包含了出錯信息。

注意,如果在打開一個雙向(two-way)socket的時候失敗,將會返回一個非致命的錯誤到調用者。

如果在一個循環中使用管道、協同進程或socket(向getline輸出或從print/printf輸入),你必須使用close()來創建新的command或socket的實例。AWK在管道、協同進程或socket返回EOF的時候不會自動關閉它們。

printf語句

AWK里的printf語句和sprintf()函數支持如下的標準格式轉換符:

格式符

含義

%c

一個ASCII字符。如果傳入的參數是一個數值,則將其轉換為字符并打印;否則,認為傳入的參數是字符串,只打印該字符串的第一個字符。

%d, %i

十進制數字(整數部分)

%e, %E

將一個浮點數以“[-]d.dddddde[+-]dd”的形式打印。%E使用大寫的E。

%f, %F

將一個浮點數以“[-]ddd.dddddd”的形式打印。使用%F(需要系統庫支持),則以大寫字母顯示"not a number"和"infinity"這類的值。

%g, %G

根據實際情況轉換為%e或%f,選擇其中最簡短的格式。%G對應%E。

%o

無符號的八進制整數。

%u

無符號的十進制整數。

%s

字符串。

%x, %X

無符號的十六進制整數。

%%

字符'%'的原意(不會取實參)。

在這些格式符中,'%'和控制字符之間可以放置如下的額外參數(和C語言中printf的格式符用法相同):

count$

位置說明符,指定對第count個參數進行格式化轉換。例如:printf("%2$d/n", 23, 24);打印的值是24。

width

指定格式化后的最短打印長度,如果不夠長則填充空格,默認右對齊。

-

左對齊,通常與width搭配使用。

0

上述不足width的話,使用前導0填充而不是空格。只對數值類型有效。

+

對數值添加正負號。只對數值類型有效。

#

以另一種形式打印格式化的內容:例如%#o會在數值前面填0;%#x、%#X會在數值前面填0x或0X;對%#e、%#E、%#f和%#F,結果總會有小數點;%#g、%#G總會顯示小數部分。

space

對正數,前面填一個空格;對負數,前面填負號。

.prec

對%e、%E、%f和%F,指明小數部分的最大位數;對%g、%G,指明有效數字的最大長度;對%d、%o、%i、%u、%x和%X,和面前的0width一樣,指明最短打印長度,不足則前面補0;對%s,指明最長打印的字符數。

另外,printf和sprintf()支持動態的width和.prec:可以從參數列表中的值作為width或.prec的值。通過在count$前面加一個*號來達到這種效果。例如:

printf("%1$*2$s/n", "Bye bye!", 12);這個例子中,1$仍然是位置說明符的作用,而*2$的意思是將第二個參數替換在這個位置,這個語句就變成了:
printf("%1$12s/n", "Bye bye!", 12);

特殊文件

在通過print、printf、getline進行I/O重定向時,gawk可識別一些特定的文件名,并且支持從gawk的父進程(通常是shell)中繼承這些文件的文件描述符來進行文件操作。同時,這些文件也可以用作命令行中的數據文件名。這些文件為:

/dev/stdin    標準輸入

/dev/stdout  標準輸出

/dev/stderr   標準錯誤輸出

/dev/fd/n      與文件描述符n關聯的文件

另外,使用破折號“-”也可以引用標準輸入,例如:

some_command | awk -f myprog.awk file1 - file2

這條命令中,awk會先讀取file1,然后讀取some_command的輸出,然后讀取file2。

下面三個特殊的文件名,可以與協同進程操作符“|&”配合使用,創建TCP/ip網絡連接。

/inet/tcp/lport/rhost/rport              本地端口是lport,與遠程主機rhost、遠程端口rport的TCP/IP連接,如果端口為0,則讓系統自己選擇端口。

/inet/udp/lport/rhost/rport      同上,只是TCP改為UDP。

/inet/raw/lport/rhost/rport             未使用,供后續擴展。

下面這些文件名用來獲取當前正在運行的gawk進程的信息:/dev/pid,/dev/ppid,/dev/pgrpid和/dev/user。目前已被棄用,改為使用PROCINFO獲取進程信息。

數值操作函數

AWK提供以下一些內置的算術函數:

函數

作用

atan2(y, x)

求y/x的反正切,單位是弧度。

cos(expr)

求余弦,expr的單位是弧度。

exp(expr)

求指數。

int(expr)

截斷expr保留整數部分。

log(expr)

求自然對數。

rand()

生成一個隨機數N,0 ≤ N < 1。

sin(expr)

求正弦,expr的單位是弧度。

sqrt(expr)

求平方根。

srand([expr])

為隨機數生成器指定一個新種子expr。如果不指定expr,就使用時間作為種子。函數的返回值是之前的種子。

字符串函數

Gawk提供以下內置的字符串函數:

              函數                                

作用

asort(s [, d])

給數組s中的成員排序(按照gawk默認的升序排序方法),排序完成后,s的數組下標改為從1開始的整數序列。

如果指定第二個參數d,則排序的結果放在數組d中,原數組s不變化。

函數的返回值是原數組s的元素個數。

asorti(s [, d])

給數組s的下標排序,(按照gawk默認的升序排序方法),排序完成后,s的數組下標改為從1開始的整數序列,而原來的下標改為數組元素的值。原來的數組元素的值被丟棄。

如果不想丟棄原來的元素的值,可以指定第二個參數d,則排序的結果放在數組d中,原數組s不變化。

函數的返回值是原數組s的元素個數。

gensub(r, s, h [, t])

對原始字符串t,將匹配正則表達式r的子串替換為s。如果字符串h以’g’或’G’開頭,則所有匹配都替換,否則只替換第一個匹配。函數的返回值即為執行替換后的字符串,也就是說,原始字符串t不會被修改。

如果不指定參數t,就從當前record中讀取,即$0。

在s中,可以通過/1~/9來指代r中第n個圓括號中的匹配項,參考下面的例子1。/0或'&'則表示整個匹配的內容。

gsub(r, s [, t])

對原始字符串t,將匹配正則表達式r的子串全部替換為s。函數的返回值為匹配到的子串的個數,也就是說,原始字符串t會被修改。

如果不指定參數t,就從當前record中讀取,即$0。

在s中,'&'則表示整個匹配的內容。如果要書寫字符’&'的原意,要寫作"//&"。

index(s, t)

返回字符串t在字符串s中第一次出現的位置。例如index(“abcdefg”, “def”)返回4,即index從1開始。如果沒找到子串t,則返回0。

length([s])

返回字符串s的長度,如果沒有參數s,則返回$0的長度。也可以傳入一個數組,這時返回數組元素的個數。

match(s, r [, a])

在字符串s中匹配正則表達式r,匹配成功則返回子串的位置(index從1開始),并更新RSTART和RLENGTH,匹配失敗則返回0;

如果有第三個參數a,則正則表達式r中每個圓括號的匹配內容會被依次賦值給數組a[1]~a[n],而整個的匹配內容則賦值給a[0]。同時,a[m, "start"]和a[m, "length"]兩個數組下標成員的值為a[m]在s中的位置和字符串長度。

見下面的例子2。

注意,如果在調用match()之前數組a不為空,則會先清空a。

split(s, a [, r])

將字符串s按照正則表達式r作為分隔符來分割,分割成的每個field保存在數組a中,函數返回fields的數量。

如果沒有r參數,則根據FS來分割。

注意,如果在調用split()之前數組a不為空,則會先清空a。

sprintf(fmt, expr-list)

根據格式fmt打印表達式列表expr-list,最終的字符串作為返回值。例如:

sprintf("name%d:%s, name%d:%s", 1, "George", 2, "Tim");

strtonum(str)

將字符串轉換為數值,可識別八進制(以0開頭)和十六進制(以0x或0X開頭)。例如strtonum(“34”),strtonum(34.50),strtonum(“017”)。

sub(r, s [, t])

同gsub,但只替換第一個匹配的子串。

substr(s, i [, n])

獲取字符串s中,從第i個字符開始的n個字符形成的子串,該子串作為返回值。參數i從1開始。

tolower(str)

將str中的大寫字母都轉換為小寫字母后作為函數返回值。

toupper(str)

將str中的小寫字母都轉換為大寫字母后作為函數返回值。

注意,gawk3.1.5支持多字節的字符,這意味著index()/length()/substr()/match()都是針對字符起作用而不是字節。

例子1,匹配“me again”,然后將其中的me改為her:

[root@ubuntu]awk_test:$ awk 'BEGIN{result=gensub("(me) (again)", "her //2", "g", "tell me again, you go again"); print result}' awk_test.txt tell her again, you go again

前面在sed命令中也講到過這種用法,不過這里圓括號不需要加反斜杠轉義,并且"/2"要寫成"//2"。

例子2,測試帶第三個參數的match函數:
[root@ubuntu]awk_test:$ awk 'BEGIN{sstr="name1:George, name2:Tim";pos=match(sstr, "name[1-9]:([a-zA-Z]*), name[1-9]:([a-zA-Z]*)", list); /print pos; print RSTART" "RLENGTH; for (i=0; i<=2; i++) print list[i], list[i, "start"], list[i, "length"];}' awk_test.txt11 23name1:George, name2:Tim, 1 23George 7 6Tim 21 3例子3,split函數測試:
[root@ubuntu]awk_test:$ awk 'BEGIN{sstr="name1:George, name2:Tim, name3:Jason";fcnt=split(sstr, list, "name[[:digit:]]+:"); print fcnt; /for (i=0; i<=fcnt; i++) print list[i];}' awk_test.txt

時間函數

由于AWK主要的用處就是處理包含時間戳信息的大型日志文件。因此AWK提供了以下時間函數來獲取和轉換時間戳:

mktime(datespec)  將datespec轉換為相對于1970-01-01零點的秒數。參數datespec的格式為:“YYYY MM DD HH MMSS[ DST]”,其中夏令時標志DST是可選的。例如mktime("2017 2 6 22 54 00")即為2017年2月6日22時54分0秒,注意這個時間是算上時區的。舉例來說,在東8區,mktime("19701 1 8 0 1")的返回值為1。

strftime([format [, timestamp[, utc-flag]]])  將timestamp轉換為format指定的格式,timestamp需為相對于1970-01-01零點的秒數。如果utc-flag不為0或null,則轉換結果是UTC時間,否則是本地時間。如果不帶timestamp參數,則使用當前時間;如果不帶timestamp和format參數,則format默認與date命令結果的格式相同,format參數可用的格式請參考C語言中的strftime()函數,也可參考Effective AWKProgramming[2]中的說明。

systime()  將當前時間轉換為相對于1970-01-01零點的秒數。

位操作函數

Gawk3.1開始的版本,提供了下面的位運算函數:

函數

作用

and(v1, v2)

按位與

compl(val)

按位取反,同C語言中的'~'運算符

lshift(val, count)     

val左移count位,即val << count

or(v1, v2)

按位或

rshift(val, count)

val右移count位(高位補符號位),即val >> count

xor(v1, v2)

按位異或

自定義函數

AWK中可以自定義函數,形式如下:

function name(parameter list) { statements }

func name(parameter list) { statements }

其中,name是函數名,前面添加關鍵字function或func。圓括號內是形參列表,后面大括號內書寫函數的實現。

例如下面的例子:

function add_INT(a, b){    return (a+b);}function add_ARRAY(array){    sum = 0;    for(i in array)    {        sum += array[i];    }    return sum;}BEGIN {aint[0]=10; aint[1]=11; aint[2]=12;print add_ARRAY(aint); print add_INT(12, 78);}

注意,對于自定義函數,在調用函數時,函數名和左圓括號之間不能有空格(AWK的內置函數沒有這個限制)。

在AWK中,所有的變量都是全局的,那么就可能出現一個函數中的局部變量和主程序的變量重名。如果想避免這種情況,可以在函數的參數列表里指明局部變量,方法是將局部變量寫在形參后面,并且用多個空格與形參列表分開。

例如:
function test(a, b,     c){    c = 20;    sum = a + b;}BEGIN {c = 10;sum = 5;test(11, 22);print c" "sum;}

這個例子中,有c和sum兩個變量,在函數test中,會修改c和sum,但是由于指明了c在test()中是作為局部變量的,因此不會影響主程序中c的值。所以print打印出的結果是“10 33”。

實際上,參數列表里的形參都會被認為是局部的,用多個空格分開的做法只是為了代碼的可讀性。

當需要將一段通用代碼制作成庫函數時,自定義函數就派上了用場,可以配合--source選項來在命令行程序中引用庫函數。

一些建議:在自定義函數內部,盡量避免定義外部可能使用的變量,例如“i”,“j”這樣的變量,在外部程序中很可能用到,因此在函數內部就不要用這樣的變量命名。建議在函數內變量命名時以“_”開頭來避免沖突。另外,變量和函數的命名盡量體現它的作用和含義。最后,如果函數內定義了外部可以使用的全局變量,變量名可以第一個字母大寫,如“Optind”以和局部變量區別(不全部大寫是為了防止和AWK內置變量混淆)。

可以參考Effective AWK Programming[2]中第10章的部分庫函數的實現和第11章的自定義函數舉例來學習自定義函數的寫法。

信號

pgawk可接收SIGUSR1和SIGHUP兩個信號。SIGUSR1信號會使pgawk生成profile文件(如之前--profile所述),包含自定義函數的調用棧,之后pgawk程序繼續運行。SIGHUP信號同樣會產生profile文件,之后pgawk程序退出。

返回值

Gawk執行正確返回EXIT_SUCCESS,通常是0;執行失敗返回EXIT_FAILURE,通常是1;如果發生嚴重錯誤,返回2,但有些系統上會返回EXIT_FAILURE。

如果exit語句指定了返回值,則gawk返回這個指定的值。

參考資料

[1] Gawk(1) manpage for GNU Awk 3.1.8

[2] Effective AWK Programming: http://www.gnu.org/software/gawk/manual/gawk.html


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 欧美精品久久天天躁 | 奶子吧naiziba.cc免费午夜片在线观看 | 精品国产一区在线 | 久久久一区二区三区四区 | 狼人狠狠干 | 水多视频在线观看 | 久久精品首页 | 欧美1区2区 | 国产成人自拍视频在线 | 国产精品成人一区 | 亚洲电影在线播放 | 成年人视频免费 | 国产好片无限资源 | 国产一级毛片视频在线! | 色婷婷a v | 99国产精品国产免费观看 | 亚洲精品在线观看网站 | 国产一级毛片高清视频完整版 | 小视频免费在线观看 | 久久久精品视 | 亚洲午夜免费 | xnxx 日本免费 | 中文字幕在线播放不卡 | 97青青草视频 | 日本不卡一区二区在线观看 | 久久国产成人精品国产成人亚洲 | 久草在线综合 | 91一区二区三区久久久久国产乱 | 久久成人动漫 | 伊人久操视频 | 一区二区精品视频 | 国产精品视频免费在线观看 | 欧美另类在线视频 | 极品国产91在线网站 | 羞羞答答www网站进入 | 午夜伊人 | 国产精品久久久久久久四虎电影 | 双性精h调教灌尿打屁股的文案 | 91成人午夜性a一级毛片 | 草久在线观看视频 | 欧美精品一区二区三区在线 |