Awk、sed與grep,俗稱Linux下的三劍客,它們之間有不少類似點,可是一樣也各有各的特點,類似的地方是它們均可以匹配文本,其中sed和awk還能夠用於文本編輯,而grep則不具有這個功用。sed是一種非交互式且面向字符流的編輯器(a "non-interactive" stream-oriented editor),而awk則是一門模式匹配的編程語言,由於它的主要功能是用於匹配文本並處理,同時它有一些編程語言纔有的語法,例如函數、分支循環語句、變量等等,固然比起咱們常見的編程語言,Awk相對比較簡單。正則表達式
使用Awk,咱們能夠作如下事情:shell
裝備以上功能,awk可以作得事情很是多。但千里之行,始於足下,咱們首先從最基本的命令行語法開始,一步一步得走入awk的編程世界。數據庫
同sed同樣,awk的命令行語法也有兩種形式:express
awk [-F ERE] [-v assignment] ... program [argument ...] awk [-F ERE] -f progfile ... [-v assignment] ...[argument ...]
這裏的program相似sed中的script,由於咱們一直強調awk是一門編程語言,因此將awk的腳本視爲一段代碼。而awk的腳本一樣能夠寫到一個文件中,並經過-f參數指定,這一點和sed是同樣的。program通常多個pattern和action序列組成,當讀入的記錄匹配pattern時,纔會執行相應的action命令。這裏有一點要注意,在第一種形式中,除去命令行選項外,program參數必定要位於第一個位置。編程
Awk的輸入被解析成多個記錄(Record),默認狀況下,記錄的分隔符是\n,所以能夠認爲一行就是一個記錄,記錄的分隔符能夠經過內置變量RS
更改。當記錄匹配某個pattern時,纔會執行後續的action命令。數組
而每一個記錄由進一步地被分隔成多個字段(Field),默認狀況下字段的分隔符是空白符,例如空格、製表符等等,也能夠經過-F ERE
選項或者內置變量FS
更改。在awk中,能夠經過$1,$2...來訪問對應位置的字段,同時$0存放整個記錄,這一點有點相似shell下的命令行位置參數。關於這些內容,咱們會在下面詳細介紹,這裏你只要知道有這些東西就好。bash
標準的awk命令行參數主要由如下三個:less
-F ERE
:定義字段分隔符,該選項的值能夠是擴展的正則表達式(ERE);-f progfile
:指定awk腳本,能夠同時指定多個腳本,它們會按照在命令行中出現的順序鏈接在一塊兒;-v assignment
:定義awk變量,形式同awk中的變量賦值,即name=value,賦值發生在awk處理文本以前;爲了便於理解,這裏舉幾個簡單的例子。經過-F參數設置冒號:爲分隔符,並打印各個字段:編程語言
[kodango@devops ~]$ echo "1:2:3" | awk -F: '{print $1 " and " $2 " and " $3}' 1 and 2 and 3
在awk的腳本中訪問經過-v選項設置的變量:編輯器
[kodango@devops ~]$ echo | awk -v a=1 'BEGIN {print a}' 1
從上面能夠看到,經過-v選項設置的變量在BEGIN
的位置就能夠訪問了。BEGIN
是一個特殊的pattern,它在awk處理輸入以前就會執行,能夠認爲是一個初始化語句,與此對應的還有END
。
好像還沒介紹如何指定處理的文件,是否是最後的argument就是指定的文件?在看我這本書以前,我也是這樣認爲的,可是實際上arguemnt有兩種形式,它們分別是輸入文件(file)和變量賦值(assignment)。
awk能夠同時指定多個輸入文件,若是輸入文件的文件名爲'-',表示從標準輸入讀取內容。
變量賦值相似-v選項,它的形式爲name=value。awk中的變量名同通常的編程語言無太多區別,可是不能同awk的保留關鍵字重名,能夠查看awk的man手冊查詢哪些是保留關鍵字。而變量值只有兩種形式:字符串和數值。變量賦值必須位於腳本參數的後面,與文件名參數無前後順序的要求,可是位於不一樣位置的賦值它的執行時機是不一樣的。
咱們用實際的例子來解釋這個區別,假設有兩個文件:a和b,它們的內容分別以下所示:
[kodango@devops awk_temp]$ cat a file a [kodango@devops awk_temp]$ cat b file b
爲了說明賦值操做發生的時機,咱們在BEGIN,正常處理,END三個地方都打印變量的值。
第一種狀況: 變量賦值位於全部文件名參數以前
[kodango@devops awk_temp]$ awk 'BEGIN {print "BEGIN: " var} {print "PROCESS: " var} \ END {print "END: " var }' var=1 a BEGIN: PROCESS: 1 END: 1
結果:賦值操做發生在正常處理以前,BEGIN
動做以後。
第二種狀況:變量賦值位於全部文件名以後:
[kodango@devops awk_temp]$ awk 'BEGIN {print "BEGIN: " var} {print "PROCESS: " var} \ END {print "END: " var }' a var=1 BEGIN: PROCESS: END: 1
結果:賦值操做發生在正常處理以後,END
動做以前。
第三種狀況:變量賦值位於文件名之間:
[kodango@devops awk_temp]$ awk 'BEGIN {print "BEGIN: " var} {print "PROCESS: " var} \ END {print "END: " var }' a var=1 b BEGIN: PROCESS: PROCESS: 1 END: 1
結果:賦值操做發生在處理前面的文件以後,而且位於處理後面的文件以前;
總結以下:
BEGIN
動做以後執行,影響到正常處理和END
動做;END
動做;END
動做;因此變量賦值必定要考慮清楚用途,不然比較容易出錯,不過通常狀況下也不會用到變量賦值。
天然地你們會將變量賦值與-v assignment選項進行比較,賦值的形式是一致的,可是-v選項的執行時機比變量賦值要早:
[kodango@devops awk_temp]$ echo 1 | awk -v var=a 'BEGIN {print "BEGIN: " var}' BEGIN: a
可見,-v選項的賦值操做在BEGIN
動做以前就執行了。
變量賦值必定要當心不要與保留關鍵字重名,不然會報錯:
[kodango@devops awk_temp]$ echo 1 | awk -v BEGIN=1 'BEGIN {print "BEGIN: " BEGIN}' awk: fatal: cannot use gawk builtin `BEGIN' as variable name
對於數據庫來講,一個數據庫表是由多條記錄組成的,每一行表示一條記錄(Record)。每條記錄由多列組成,每一列表示一個字段(Field)。Awk將一個文本文件視爲一個文本數據庫,所以它也有記錄和字段的概念。默認狀況下,記錄的分隔符是回車,字段的分隔符是空白符,因此文本文件的每一行表示一個記錄,而每一行中的內容被空白分隔成多個字段。利用字段和記錄,awk就能夠很是靈活地處理文件的內容。
能夠經過-F選項來修改默認的字段分隔符,例如/etc/passwd的每一行都是由冒號分隔成多個字段的,因此這裏就須要將分隔符設置成冒號:
[kodango@devops awk_temp]$ awk -F: '{print $1}' /etc/passwd | head -3 root bin daemon
這裏經過$1引用第一人字段,相似地$2表示第二個字段,$3表示第三個字段.... $0則表示整個記錄。內置變量NF記錄着字段的個數,因此$NF表示最後一個字段:
[kodango@devops awk_temp]$ awk -F: '{print $NF}' /etc/passwd | head -3 /bin/bash /bin/false /bin/false
固然,$(NF-1)表示倒數第二個。
內置變量FS也能夠用於更改字段分隔符,它記錄着當前的字段分隔符:
[kodango@devops awk_temp]$ awk -F: '{print FS}' /etc/passwd | head -1 : [kodango@devops awk_temp]$ awk -v FS=: '{print $1}' /etc/passwd | head -1 root
記錄的分隔符能夠經過內置變量RS更改:
[kodango@devops awk_temp]$ awk -v RS=: '{print $0}' /etc/passwd | head -1 root
若是將RS設置成空,行爲有就一點怪異了,它會將連續不爲空行的全部行(一個段落)看成一個記錄,並且強制回車爲字段分隔符:
[kodango@devops awk_temp]$ cat awk_man.txt The awk utility shall execute programs written in the awk programming language, which is specialized for textual data manipulation. An awk program is a sequence of patterns and corresponding actions. When input is read that matches a pattern, the action associated with that pattern is carried out. Input shall be interpreted as a sequence of records. By default, a record is a line, less its terminating <newline>, but this can be changed by using the RS built-in variable. Each record of input shall be matched in turn against each pattern in the program. For each pattern matched, the associated action shall be executed. [kodango@devops awk_temp]$ awk 'BEGIN {RS="";FS=":"} {print "First line: " $1}' awk_man.txt First line: The awk utility shall execute programs written in the awk programming language, First line: Input shall be interpreted as a sequence of records. By default, a record is a line,
這裏,咱們將變量賦值放到BEGIN
動做中執行,由於BEGIN
動做是在文件處理以前執行的,專門用於放初始化的語句。FS的賦值在這裏是無效的,awk依然使用回車符來分隔字段。
命令行中的program部分,能夠稱爲awk代碼,也能夠稱爲awk腳本。一段awk腳本是由多個'pattern { action }
'序列組成的。action是一個或者多個語句,它在輸入行匹配pattern的時候被執行。若是pattern爲空,代表這個action會在每一行處理時都會被執行。下面的例子簡單地打印文件的每一行,這裏不帶任何參數的print語句打印的是整個記錄,相似'print $0
':
[kodango@devops awk_temp]$ echo -e 'line1\nline2' | awk '{print}' line1 line2
除了
,還能夠在腳本中定義自定義的函數,函數定義格式以下所示:pattern { action }
function name(parameter list) { statements }
函數的參數列表用逗號分隔,參數默認是局部變量,沒法在函數以外訪問,而在函數中定義的變量爲全局變量,能夠在函數以外訪問,如:
[kodango@devops awk_temp]$ echo line1 | awk ' function t(a) { b=a; print a; } { print b; t("kodango.me"); print b; }' kodango.me kodango.me
Awk腳本中的語句使用空行或者分號分隔,使用分號能夠放在同一行,不過有時候會影響可讀性,尤爲是分支或循環結構中,很容易出錯。
若是Awk中的一個語句太長,要分紅多行,能夠在行爲使用反斜槓'\':
[kodango@devops awk_temp]$ cat test.awk function t(a) { b=a print "This is a very long line, so use backslash to escape the newline \ then we will print the variable a: a=" a } { print b; t("kodango.me"); print b;} [kodango@devops awk_temp]$ echo 1 | awk -f test.awk This is a very long line, so use backslash to escape the newline then we will print the variable a: a=kodango.me kodango.me
這裏咱們將腳本寫到文件中,並經過-f參數來指定。可是,在一些特殊符號以後,是能夠直接換行的,例如", { && ||"。
模式是awk中比較重要的一部分,它有如下幾種狀況:
/regular expression/
: 擴展的正則表達式(Extended Regular Expression), 關於ERE能夠參考這篇文章;relational expression
: 關係表達式,例如大於、小於、等於,關係表達式結果爲true表示匹配;BEGIN
: 特殊的模式,在第一個記錄處理以前被執行,經常使用於初始化語句的執行;END
: 特殊的模式,在最後一個記錄處理以前被執行,經常使用於輸出彙總信息;pattern, pattern
:模式對,匹配二者之間的全部記錄,相似sed的地址對;例如查找匹配數字3的行:
[kodango@devops awk_temp]$ seq 1 20 | awk '/3/ {print}' 3 13
相反地,能夠在在正則表達式以前加上'!'表示不匹配:
[kodango@devops awk_temp]$ seq 1 5 | awk '!/3/ {print}' 1 2 4 5
除了BEGIN
和END
這兩個特殊的模式外,其他的模式均可以使用'&&'或者'||'運算符組合,前者表示邏輯與,後者表示邏輯或:
[kodango@devops awk_temp]$ seq 1 50 | awk '/3/ && /1/ {print}' 13 31
前面的正則都是整行匹配,有時候僅僅須要匹配某個字符,這樣咱們能夠用表達式$n ~ /ere/
:
[kodango@devops ~]$ awk '$1 ~ /ko/ {print}' /etc/passwd kodango:x:1000:1000::/home/kodango:/bin/bash
有時候咱們只想顯示特定和行,例如顯示第一行:
[kodango@devops ~]$ seq 1 5 | awk 'NR==1 {print}' 1
和sed篇同樣,這裏我不會詳細介紹正則表達式。由於正則表達式的內容介紹起來太麻煩,仍是推薦同窗閱讀現有的文章(如Linux/Unix工具與正則表達式的POSIX規範),裏面對各個流派的正則表達式概括地很清楚了。
表達式能夠由常量、變量、運算符和函數組成,常數和變量的值能夠爲字符串和數值。
Awk中的變量有三種類型:用戶定義的變量,內置變量和字段變量。其中,內置變量名都是大寫的。
變量並不非必定要被聲明或者被初始化,一個變量默認的值是空字符串,只是在某些上下文上會隱式的自動轉換成數字0(例如數學運算),記住awk中的變量是無類型的,不存在字符串變量仍是數字變量的區別,只是有時候爲了解說方便,纔會這麼說。(感謝網友@紫雲妃的提醒)
字段變量能夠用$n來引用,n的取值範圍爲[0,NF]。n能夠爲一個變量,例如$NF代碼最後一個字段,而$(NF-1)表示倒數第二個字段。
數組是一種特殊的變量,awk中的數組都是關聯數組,它的下標都是字符串值(man手冊中的原話是:All arrays in AWK are associative, i.e. indexed by string values),即便你使用的下標是一個數字,awk也會將下標隱式轉換成字符串。因此容易給人一個誤解,數組的下標能夠是數字或者字符串。
數組的賦值很簡單,下面將value賦值給數組下標爲index的元素:
array[index]=value
能夠用for..in..語法遍歷數組元素,其中item是數組元素對應的下標:
for (item in array)
固然也能夠在if分支判斷中使用in操做符:
if (item in array)
一個完整的例子以下所示:
[kodango@devops ~]$ echo "1 2 3" | awk '{ for (i=0;i<NF;i++) a[i]=i; } END { print 3 in a for (i in a) printf "%s: %s\n", i, a[i]; }' 0 0: 0 1: 1 2: 2
Awk在內部維護了許多內置變量,或者稱爲系統變量,例如以前提到的FS
、RS
等等。常見的內置變量以下表所示
變量名 | 描述 |
---|---|
ARGC | 命令行參數的各個,即ARGV數組的長度 |
ARGV | 存放命令行參數 |
CONVFMT | 定義awk內部數值轉換成字符串的格式,默認值爲"%.6g" |
OFMT | 定義輸出時數值轉換成字符串的格式,默認值爲"%.6g" |
ENVIRON | 存放系統環境變量的關聯數組 |
FILENAME | 當前被處理的文件名 |
NR | 記錄的總個數 |
FNR | 當前文件中的記錄的總個數 |
FS | 字段分隔符,默認爲空白 |
NF | 每一個記錄中字段的個數 |
RS | 記錄的分隔符,默認爲回車 |
OFS | 輸出時字段的分隔符,默認爲空白 |
ORS | 輸出時記錄的分隔符,默認爲回車 |
RLENGTH | 被match函數匹配的子串長度 |
RSTART | 被match函數匹配的子串位於目標字符串的起始下標 |
下面主要介紹幾個比較難理解的內置變量:
1. ARGV
與ARGC
ARGV
與ARGC
的意思比較好理解,就像C語言main(int argc, char **argv)
。ARGV
數組的下標從0開始到ARGC
-1,它存放的是命令行參數,而且排除命令行選項(例如-v/-f)以及program部分。所以事實上ARGV
只是存儲argument的部分,即文件名(file)以及命令行變量賦值兩部分的內容。
經過下面的例子能夠大概瞭解ARGC與ARGV的用法:
[kodango@devops awk_temp]$ awk 'BEGIN { > for (i = 0; i < ARGC; i++) > print ARGV[i] > }' inventory-shipped BBS-list awk inventory-shipped BBS-list
ARGV
的用法不只限於此,它是能夠修改的,能夠更改數組元素的值,能夠增長數組元素或者刪除數組元素。
a. 更改ARGV
元素的值
假設咱們有a, b兩個文件,它們各有一行內容:file a和file b。如今利用ARGV,咱們能夠作到偷樑換柱:
[kodango@devops awk_temp]$ awk 'BEGIN{ARGV[1]="b"} {print}' a file b
這裏要注意ARGV[1]="b"
的引號不能缺乏,不然ARGV[1]=b
會將變量b的值賦值給ARGV[1]
。
當awk處理完一個文件以後,它會從ARGV
的下一個元素獲取參數,若是是一個文件則繼續處理,若是是一個變量賦值則執行賦值操做:
[kodango@devops awk_temp]$ awk 'BEGIN{ARGV[1]="var=1"} {print var}' a b 1
爲何這裏只打印一次變量值呢?能夠回頭再看看上一篇中介紹變量賦值的內容。
而當下一個元素爲空時,則跳過不處理,這樣能夠避開處理某個文件:
[kodango@devops awk_temp]$ awk 'BEGIN{ARGV[1]=""} {print}' a b file b
上面的例子中a這個文件就被跳過了。
而當下一個元素的值爲"-"時,代表從標準輸入讀取內容:
[kodango@devops awk_temp]$ awk 'BEGIN{ARGV[1]="-"} {print}' a b a a # --> 這裏按下CTRL+D中止輸入 file b
b. 刪除ARGV
元素
刪除ARGV
元素和將元素的值賦值爲空的效果是同樣的,它們都會跳轉對某個參數的處理:
[kodango@devops awk_temp]$ awk 'BEGIN{delete ARGV[1]} {print}' a b file b
刪除數組元素能夠用delete
語句。
c. 增長ARGV
元素
我第一次看到ARGV
變量的時候就在想,能不能利用ARGV
變量避免提供命令行參數,就像這樣:
awk 'BEGIN{ARGV[1]="a";} {print}'
可是事實上這樣不行,awk會依然從標準輸入中獲取內容。下面的方法卻是能夠,首先增長ARGC
的值,再增長ARGV
元素,我到如今也沒搞懂這二者的區別:
[kodango@devops awk_temp]$ awk 'BEGIN{ARGC+=1;ARGV[1]="a"} {print}' file a
2. CONVFMT
與OFMT
Awk中容許數值到字符串相互轉換,其中內置變量CONVFMT
定義了awk內部數值到字符串轉換的格式,它的默認值爲"%.6g":
[kodango@devops awk_temp]$ awk 'BEGIN { printf "CONVFMT=%s, num=%f, str=%s\n", CONVFMT, 12.11, 12.11 }' CONVFMT=%.6g, num=12.110000, str=12.11
經過更改CONVFMT
,咱們能夠定義本身的轉換格式:
[kodango@devops awk_temp]$ awk 'BEGIN { CONVFMT="%d"; printf "CONVFMT=%s, num=%f, str=%s\n", CONVFMT, 12.11, 12.11 }' CONVFMT=%d, num=12.110000, str=12
與此對應地還有一個內置變量OFMT,它與CONVFMT
的做用是相似的,只不過是影響輸出的時候數字轉換成字符串的格式:
[kodango@devops awk_temp]$ awk 'BEGIN { OFMT="%d";print 12.11 }' 12
3. ENVIRON
ENVIRON
是一個存放系統環境變量的關聯數組,它的下標是環境變量名稱,值是相應環境變量的值。例如:
[kodango@devops awk_temp]$ awk 'BEGIN { print ENVIRON["USER"] }' kodango
利用環境變量也能夠將值傳遞給awk:
[kodango@devops awk_temp]$ U=hello awk 'BEGIN { print ENVIRON["U"] }' hello
能夠利用for..in循環遍歷ENVIRON
數組:
[kodango@devops awk_temp]$ awk 'BEGIN { for (env in ENVIRON) printf "%s=%s\n", env, ENVIRON[env]; }'
4. RLENGTH
與RSTART
RLENGTH
與RSTART
都是與match
函數相關的,前者表示匹配的子串長度,後者表示匹配的子串位於目標字符串的起始下標。例如:
[kodango@devops ~]$ awk 'BEGIN {match("hello,world", /llo/); print RSTART,RLENGTH}' 3 3
關於match
函數,咱們會在之後介紹。
表達式中必然少不了運算符,awk支持的運算符能夠參見man手冊中的「Expressions in awk」一小節內容:
[kodango@devops awk_temp]$ man awk | grep "^ *Table: Expressions in" -A 42 | sed 's/^ *//' Table: Expressions in Decreasing Precedence in awk Syntax Name Type of Result Associativity ( expr ) Grouping Type of expr N/A $expr Field reference String N/A ++ lvalue Pre-increment Numeric N/A -- lvalue Pre-decrement Numeric N/A lvalue ++ Post-increment Numeric N/A lvalue -- Post-decrement Numeric N/A expr ^ expr Exponentiation Numeric Right ! expr Logical not Numeric N/A + expr Unary plus Numeric N/A - expr Unary minus Numeric N/A expr * expr Multiplication Numeric Left ...如下省略...
到目前爲止,用得比較多的語句就是print
,其它的還有printf、delete、break、continue、exit、next
等等。這些語句與函數不一樣的是,它們不會使用帶括號的參數,而且沒有返回值。不過也有意外,好比printf
就能夠像函數同樣的調用:
[kodango@devops awk_temp]$ echo 1 | awk '{printf("%s\n", "abc")}' abc
break
和continue
語句,你們應該比較瞭解,分別用於跳出循環和跳到下一個循環。
delete
用於刪除數組中的某個元素,這個咱們在上面介紹ARGV
的時候也使用過。
exit
的用法顧名思義,就是退出awk的處理,而後會執行END
部分的內容:
[kodango@devops awk_temp]$ echo $'line1\nline2' | awk '{print;exit} END {print "exit.."}' line1 exit..
next
語句相似sed的n命令,它會讀取下一條記錄,並從新回到腳本的最開始處執行:
[kodango@devops awk_temp]$ echo $'line1\nline2' | awk '{ > print "Before next.." > print $0 > next > print "After next.." > }' Before next.. line1 Before next.. line2
從上面能夠看出next
後面的print語句不會執行。
print與printf語句是使用最多的,它們將內容輸出到標準輸出。注意在print語句中,輸出的變量之間帶不帶逗號是有區別的:
[kodango@devops awk_temp]$ echo "1 2" | awk '{print $1, $2}' 1 2 [kodango@devops awk_temp]$ echo "1 2" | awk '{print $1 $2}' 12
print輸出時,字段之間的分隔符能夠由OFS從新定義:
[kodango@devops awk_temp]$ echo "1 2" | awk '{OFS=";";print $1,$2}' 1;2
除此以外,print的輸出還能夠重定向到某個文件中或者某個命令:
print items > output-file print items >> output-file print items | command
假設有這同樣一個文件,第一列是語句名稱,第二列是對應的說明:
[kodango@devops awk_temp]$ cat column.txt statement|description delete|delete item from an array exit|exit from the awk process next|read next input record and process
如今咱們要將兩列的內容分別輸出到statement.txt和description.txt兩個文件中:
[kodango@devops awk_temp]$ awk -F'|' '{ > print $1 > "statement.txt"; > print $2 > "description.txt" > }' column.txt [kodango@devops awk_temp]$ cat statement.txt statement delete exit next [kodango@devops awk_temp]$ cat description.txt description delete item from an array exit from the awk process read next input record and process
下面是一個重定向到命令的例子,假設咱們要對下面的文件進行排序:
[kodango@devops awk_temp]$ cat num.list 1 3 2 9 5
能夠經過將print的內容重定向到"sort -n"命令:
[kodango@devops awk_temp]$ awk '{print | "sort -n"}' num.list 1 2 3 5 9
printf命令的用法與print相似,也能夠重定向到文件或者輸出,只不過printf比print多了格式化字符串的功能。printf的語法也大多數語言包括bash的printf命令相似,這裏就很少介紹了。
awk的函數分紅數學函數、字符串函數、I/O處理函數以及用戶自定義的函數,其中用戶自定義的函數咱們在上一篇中也有簡單的介紹,下面咱們一一來介紹這幾類函數。
awk中支持如下數學函數:
atan2(y,x)
:反正切函數;cos(x)
:餘弦函數;sin(x)
:正弦函數;exp(x)
:以天然對數e爲底指數函數;log(x)
:計算以e 爲底的對數值;sqrt(x)
:絕對值函數;int(x)
:將數值轉換成整數;rand()
:返回0到1的一個隨機數值,不包含1;srand([expr])
:設置隨機種子,通常與rand函數配合使用,若是參數爲空,默認使用當前時間爲種子;例如,咱們使用rand()
函數生成一個隨機數值:
[kodango@devops awk_temp]$ awk 'BEGIN {print rand(),rand();}' 0.237788 0.291066 [kodango@devops awk_temp]$ awk 'BEGIN {print rand(),rand();}' 0.237788 0.291066
可是你會發現,每次awk執行都會生成一樣的隨機數,可是在一次執行過程當中產生的隨機數又是不一樣的。由於每次awk執行都使用了一樣的種子,因此咱們能夠用srand()
函數來設置種子:
[kodango@devops awk_temp]$ awk 'BEGIN {srand();print rand(),rand();}' 0.171625 0.00692412 [kodango@devops awk_temp]$ awk 'BEGIN {srand();print rand(),rand();}' 0.43269 0.782984
這樣每次生成的隨機數就不同了。
利用rand()
函數咱們也能夠生成1到n的整數:
[kodango@devops awk_temp]$ awk ' > function randint(n) { return int(n*rand()); } > BEGIN { srand(); print randint(10); > }' 3
awk中包含大多數常見的字符串操做函數。
sub(ere, repl[, in])
描述:簡單地說,就是將in中匹配ere的部分替換成repl,返回值是替換的次數。若是in參數省略,默認使用$0。替換的動做會直接修改變量的值。
下面是一個簡單的替換的例子:
[kodango@devops ~]$ echo "hello, world" | awk '{print sub(/ello/, "i"); print}' 1 hi, world
在repl參數中&是一個元字符,它表示匹配的內容,例如:
[kodango@devops ~]$ awk 'BEGIN {var="kodango"; sub(/kodango/, "hello, &", var); print var}' hello, kodango
gsub(ere, repl[, in])
描述:同sub()
函數功能相似,只不過是gsub()
是全局替換,即替換全部匹配的內容。
index(s, t)
描述:返回字符串t在s中出現的位置,注意這裏位置是從1開始計算的,若是沒有找到則返回0。
例如:
[kodango@devops ~]$ awk 'BEGIN {print index("kodango", "o")}' 2 [kodango@devops ~]$ awk 'BEGIN {print index("kodango", "w")}' 0
length[([s])]
描述:返回字符串的長度,若是參數s沒有指定,則默認使用$0做爲參數。
例如:
[kodango@devops ~]$ awk 'BEGIN {print length('kodango');}' 0 [kodango@devops ~]$ echo "first line" | awk '{print length();}' 10
match(s, ere)
描述: 返回字符串s匹配ere的起始位置,若是不匹配則返回0。該函數會定義RSTART
和RLENGTH
兩個內置變量。RSTART
與返回值相同,RLENGTH
記錄匹配子串的長度,若是不匹配則爲-1。
例如:
[kodango@devops ~]$ awk 'BEGIN { print match("kodango", /dango/); printf "Matched at: %d, Matched substr length: %d\n", RSTART, RLENGTH; }' 3 Matched at: 3, Matched substr length: 5
split(s, a[, fs])
描述:將字符串按照分隔符fs,分隔成多個部分,並存到數組a中。注意,存放的位置是從第1個數組元素開始的。若是fs爲空,則默認使用FS分隔。函數返回值分隔的個數。
例如:
[kodango@devops ~]$ awk 'BEGIN { > split("1;2;3;4;5", arr, ";") > for (i in arr) > printf "arr[%d]=%d\n", i, arr[i]; > }' arr[4]=4 arr[5]=5 arr[1]=1 arr[2]=2 arr[3]=3
這裏有一個奇怪的地方是for..in..輸出的數組不是按順序輸出的,若是要按順序輸出能夠用常規的for循環:
[kodango@devops ~]$ awk 'BEGIN { > split("1;2;3;4;5", arr, ";") > for (i=0;^C [kodango@devops ~]$ awk 'BEGIN { > n=split("1;2;3;4;5", arr, ";") > for (i=1; i<=n; i++) > printf "arr[%d]=%d\n", i, arr[i]; > }' arr[1]=1 arr[2]=2 arr[3]=3 arr[4]=4 arr[5]=5
sprintf(fmt, expr, expr, ...)
描述:相似printf,只不過不會將格式化後的內容輸出到標準輸出,而是看成返回值返回。
例如:
[kodango@devops ~]$ awk 'BEGIN { > var=sprintf("%s=%s", "name", "value") > print var > }' name=value
substr(s, m[, n])
描述:返回從位置m開始的,長度爲n的子串,其中位置從1開始計算,若是未指定n或者n值大於剩餘的字符個數,則子串一直到字符串末尾爲止。
例如:
[kodango@devops ~]$ awk 'BEGIN { print substr("kodango", 2, 3); }' oda [kodango@devops ~]$ awk 'BEGIN { print substr("kodango", 2); }' odango
tolower(s)
描述:將字符串轉換成小寫字符。
例如:
[kodango@devops ~]$ awk 'BEGIN {print tolower("KODANGO");}' kodango
toupper(s)
描述:將字符串轉換成大寫字符。
例如
[kodango@devops ~]$ awk 'BEGIN {print tolower("kodango");}' KODANGO
getline
getline
的用法相對比較複雜,它有幾種不一樣的形式。不過它的主要做用就是從輸入中每次獲取一行輸入。
a. expression | getline [var]
這種形式將前面管道前命令輸出的結果做爲getline
的輸入,每次讀取一行。若是後面跟有var,則將讀取的內容保存到var變量中,不然會從新設置$0和NF
。
例如,咱們將上面的statement.txt文件的內容顯示做爲getline
的輸入:
[kodango@devops awk_temp]$ awk 'BEGIN { while("cat statement.txt" | getline var) print var}' statement delete exit next
上面的例子中命令要用雙引號,"cat statement.txt
",這一點同print/printf
是同樣的。
若是不加var,則直接寫到$0中,注意NF
值也會被更新:
[kodango@devops awk_temp]$ awk 'BEGIN { while("cat statement.txt" | getline) print $0,NF}' statement 1 delete 1 exit 1 next 1
b. getline [var]
第二種形式是直接使用getline
,它會從處理的文件中讀取輸入。一樣地,若是var沒有,則會設置$0,而且這時候會更新NF
, NR
和FNR
:
[kodango@devops awk_temp]$ awk '{ > while (getline) > print NF, NR, FNR, $0; > }' statement.txt 1 2 2 delete 1 3 3 exit 1 4 4 next
c. getline [var] < expression
第三種形式從expression中重定向輸入,與第一種方法相似,這裏就不加贅述了。
close
close
函數能夠用於關閉已經打開的文件或者管道,例如getline
函數的第一種形式用到管道,咱們能夠用close
函數把這個管道關閉,close
函數的參數與管道的命令一致:
[kodango@devops awk_temp]$ awk 'BEGIN { while("cat statement.txt" | getline) { print $0; close("cat statement.txt"); }}' statement statement statement statement statement
可是每次讀了一行後,關閉管道,而後從新打開又從新讀取第一行就死循環了。因此要慎用,通常狀況下也不多會用到close
函數。
system
這個函數很簡單,就是用於執行外部命令,例如:
[kodango@devops awk_temp]$ awk 'BEGIN {system("uname -r");}' 3.6.2-1-ARCH
快速瞭解Awk系列的幾篇文章相對比較粗糙,我是參考Awk的man手冊以及《Sed & wk》附錄B總結而成的,可是應該可讓你們對awk有一個大體的瞭解,歡迎你們一塊兒交流。
附Peteris Krumins在他博客上發佈的一份cheet sheet:awk cheat sheet (.pdf)