第十九章 文本處理流編輯器:awk編程
名詞解釋
awk 是一種編程語言,用於linux/unix下對文本和數據進行處理。數據可以來自標準輸入(stdin)、一個或多個文件、或其它命令的輸出。它支持用戶自定義函數和動態正則表達式等先進功能,是linux/unix下的一個強大個編程工具。它在命令行中使用,但更多是作為腳本來使用。awk有很多內建的功能,比如:數組、函數等,這是它和C語言的相同之處,靈活性是awk最大的優勢。
awk命令格式和選項
語法形式
awk [option] ‘script‘ var=value file(s) awk [option] -f scriptfile var=value file(s)
常用命令選項
- -F fs :fs指定輸入分隔符,fs可以是字符串或正則表達式,如: -F:。
- -v var=value :賦值一個用戶定義變量,將外部變量傳遞給awk。
- -f scriptfile :從腳本文件中讀取awk命令。
- -m[fr] val :對val值設置內在限制,-mf選項限制分配給val的最大塊數目;-mr選項限制記錄的最大數目。這兩個功能是Bell實驗室版awk的擴展功能,在標準awk中不適用。
awk模式和操作
awk腳本是由模式和操作組成的
模式
模式可以是以下任意一個:
- /正則表達式/ :適用通配符的擴展集。
- 關系表達式:適用運算符進行操作,可以是字符串或數字的比較測試。
- 模式匹配表達式:用運算符
~
(匹配) 和~!
(不匹配)。 - BEGIN語句塊、pattern語句塊、END語句塊:參見awk的工作原理:見本章後邊章節。
操作
操作由一個或多個命令、函數、表達式組成,之間由換行符或分號隔開,並位於大括號內,主要部分是:
- 變量或數組賦值
- 輸出命令
- 內置函數
- 控制流語句
awk腳本基本結構
awk ‘BEGIN{ print "start" } pattern{ commands } END{ print "end"}‘ file
一個awk腳本通常由:BEGIN語句塊、能夠使用模式匹配的通用語句塊、END語句塊 三部分組成,這三個部分是可選的。
任意一個部分都可以不出現在腳本中,腳本通常是在 單引號或雙引號中,例如:
#腳本在單引號中:
awk ‘BEGIN{ i=0 } { i++ } END{ print i }‘ filename
#腳本在雙引號中:
awk "BEGIN{ i=0 } { i++ } END{ print i }" filename
awk的工作原理
awk ‘BEGIN{ commands } pattern{ commands } END{ commands }‘
- 第一步:執行
BEGIN{ commands }
語句塊中的語句。 - 第二步:從文件或標準輸入(stdin)讀取一行,然後執行
pattern{ commands }
語句塊,它逐行描述文件,從第一行到最後一行重復這個過程,知道文件全部被讀取完畢。 - 第三步:當讀至輸入流末尾時,執行E
ND{ commands }
語句塊。
BEGIN語句塊在awk開始 從輸入流中 讀取之前被執行,這是一個可選的語句塊,比如:變量初始化、打印輸出表格的表頭等語句通常可以寫在BEGIN語句塊中。
END語句塊在awk從輸入流中 讀取完所有的行之後即被執行,比如:打印所有行的分析結果這類信息 都是在END語句塊中完成,它也是一個可選語句塊。
pattern語句塊中的通用命令是最重要的部分,它也是可選的。如果沒有提供pattern語句塊,則默認執行{ print },即打印每一個讀取的行,awk讀取的每一行都會執行該語句塊。
示例
echo -e "A line 1\nA line 2" | awk ‘BEGIN{ print "start:" } { print } END{ print "end:" }‘
輸出:
start:
A line 1
A line 2
end:
當使用不帶參數print時,它就打印當前行,當print的參數是以逗號進行分割時,打印時則以空格作為定界符。在awk中的print語句塊中雙引號是被當作拼接符使用,例如:
echo | awk ‘{ var1="v1";var2="v2";var3="v3"; print var1,var2,var3}‘
結果:
v1 v2 v3
雙引號拼接使用:
echo | awk ‘{ var1="v1";var2="v2";var3="v3"; print var1"="var2"="var3}‘
結果 :
v1=v2=v3
{}類似一個循環體,會對文件中的每一行進行叠代,通常變量初始化語句(如:i=0)以及打印文件頭部的語句放入BEGIN語句塊中,將打印的結果等語句放在END語句塊中。
awk內置變量(預定義變量)
說明:[][][A][N][P][G]
表示第一個支持變量的工具,[A]=awk、[N]=nawk、[P]=POSIXawk、[G]=gawk
$n :當前記錄的第n個字段,比如n為1表示第一個字段,n為2表示第二個字段。
$0 :這個變量 包含執行過程中 當前行的文本內容。
[N]:ARGC 命令行參數的數目。
[G]:ARGIND 命令行中當前文件的位置(從0開始計算)。
[N]:ARGV 包含命令行參數的數組。
[G]:CONVFMT 數字轉換格式(默認值為%.6g)
[P]:ENVIRON 環境變量關聯數組。
[N]:ERRNO 最後一個系統錯誤的描述。
[G]:FIELDWIDTHS 字段寬度列表(用空格鍵分隔)。
[A]:FILENAME 當前輸入文件的名。
[P]:FNR 同NR,但相對於當前文件。
[A]:FS字段分隔符(默認是任何空格)。
[G]:IGNORECASE 如果為真,則進行忽略大小寫的匹配。
[A]:NF 打印當前行的最後一個字段。
[A]:NR 打印當前行的行號。
[A]:OFMT 數字的輸出格式(默認值是%.6g)。
[A]:OFS 輸出字段分隔符(默認值是一個空格)。
[A]:ORS 輸出記錄分隔符(默認值時一個換行符)。
[A]:RS 記錄分隔符(默認是一個換行符)。
[N]:RSTART 由match函數所匹配的字符串的第一個位置。
[N]:RLENGTH 由match函數所匹配的字符串的長度。
[N]:SUBSEP 數組下標分隔符(默認值是34)。
示例:
echo -e "line1 f1 f1\nline2 f2 f2\nline3 f3 f3" | awk ‘{print "Line No:"NR",No of field:"NF, "$0="$0, "$1="$1, "$2="$2, "$3="$3}‘
輸出:
Line No:1,No of field:3 $0=line1 f1 f1 $1=line1 $2=f1 $3=f1
Line No:2,No of field:3 $0=line2 f2 f2 $1=line2 $2=f2 $3=f2
Line No:3,No of field:3 $0=line3 f3 f3 $1=line3 $2=f3 $3=f3
解釋:
NR:表示打印當前讀取內容所在的行號
NF:表示打印當前行的最後一個字段
使用print $NF
可以打印出一行中的最後一個字段,使用$(NF-1)
則是倒數第二個字段,其他依次類推:
[root@ceshi awk]# echo -e "line1 f2 f3\nline2 f4 f5" | awk ‘{print $NF}‘
f3
f5
[root@ceshi awk]# echo -e "line1 f2 f3\nline2 f4 f5" | awk ‘{print $(NF-1)}‘
f2
f4
打印每一行的第二和第三個字段:
[root@ceshi awk]# echo -e "line1 f2 f3\nline2 f4 f5" | awk ‘{print $2,$3}‘
f2 f3
f4 f5
統計文件中的行數:
[root@ceshi awk]# echo -e "line1 f2 f3\nline2 f4 f5" | awk ‘END{print NR}‘
2
以上命令只使用了END語句塊,在讀入每一行的時候,awk會將NR更新為對應的行號,當到達最後一行NR的值就是最後一行的行號,所以END語句塊中的NR就是文件的行數。
計算每一行中的第一個字段累加的例子:
[root@ceshi awk]# seq 5 | awk ‘BEGIN{ sum=0; print "計算總和:" } {print $1"+"; sum+=1 } END{print "相加總和是:"sum}‘
計算總和:
1+
2+
3+
4+
5+
相加總和是:5
將外部變量傳遞給awk
借助-v選項,可以將外部值(並非stdin)傳遞給awk:
[root@ceshi awk]# abc=2000
[root@ceshi awk]# echo | awk -v def=$abc ‘{ print def }‘
2000
另一種傳遞外部變量方法:
[root@ceshi awk]# echo | awk ‘{ print v1,v2 }‘ v1=$var1 v2=$var2
hello world
當輸入來自於文件時,使用:
awk ‘{ print v1,v2 }‘ v1=$var1 v2=$var2 filename
#此處有問題。
以上方法中,變量之間用空格分隔作為awk的命令行參數 跟隨在BEGIN、{}和END語句之後。
awk運算與判斷
awk支持多種運算,還提供了內置的運算函數(如log、sqr、cos、sin等)和一些用於對字符串進行操作的函數(如length、substr等)。awk中也提供了關系判斷,允許多種測試作為樣式匹配,還提供了模式匹配表達式 ~(匹配)和 ~!(不匹配)。awk也支持用邏輯運算符。
算術運算符
運算符 | 描述 |
---|---|
+ - | 加,減 |
* / & | 乘,除,求余 |
+ - ! | 一元加,減和邏輯非 |
^ *** | 求冪 |
++ -- | 增加或減少,作為前綴或後綴 |
例:
[root@ceshi awk]# awk ‘BEGIN{a="b"; print a++,++a;}‘
0 2
註意:所有用作算術運算符進行操作,操作數自動轉為數值,所有非數值都變為0; 裏邊的++就等於 +1;a++就等於0+1。
再看下面的例子:
[root@ceshi awk]# awk ‘BEGIN{a="b"; print a++,a,++a,++a;}‘
0 1 2 3
賦值運算符
運算符 | 描述 |
---|---|
= | 簡單的賦值運算符 |
+= | 加法賦值運算符 |
-= | 減法賦值運算符 |
*= | 乘法賦值運算符 |
/= | 除法賦值運算符 |
%= | 取模賦值運算符 |
^= | 二進制位異或運算 |
**= | 乘冪賦值運算符 |
例:
a+=5;等同於: a=a+5 ;其他運算符也同樣
邏輯運算符
運算符 | 描述 |
---|---|
|| | 邏輯 或 |
&& | 邏輯 與 |
例如:
[root@ceshi awk]# awk ‘BEGIN{a=1;b=2;print (a>5 && b<=2),(a>5 || b<=2);}‘
0 1
&&:代表兩邊條件必須都滿足
||:達標兩邊條件一個滿足即可
關系運算符
運算符 | 描述 |
---|---|
< | 小於 |
<= | 小於等於 |
> | 大於 |
>= | 大於等於 |
!= | 不等於 |
== | 等於 |
例如:
[root@ceshi awk]# awk ‘BEGIN{a=11; if(a>=9){print "ok";}}‘
ok
註意:> < 可以作為字符串比較,也可以作為數值比較,關鍵看操作符。如果是字符串會轉換為字符串比較。兩個都為數字才轉為數值比較。字符串比較:是按照ASCII碼的對應的順序比較。
其它運算符
運算符 | 描述 |
---|---|
$ | 字段引用 |
空格 | 字符串連接符 |
?: | C條件表達式 |
in | 數組中是否存在某鍵值 |
not in | 數組中不存在某鍵值 |
例如:
[root@ceshi awk]# awk ‘BEGIN{ print 5==5?"ok":"err";}‘
ok
[root@ceshi awk]# awk ‘BEGIN{ print 5==6?"ok":"err";}‘
err
[root@ceshi awk]# awk ‘BEGIN{a="b"; arr[0]="b"; arr[b]="c"; print (a in arr);}‘
0
[root@ceshi awk]# awk ‘BEGIN{a="b"; arr[0]="b"; arr[b]="c"; print (b in arr);}‘
1
運算級別優先級表
級別越高越優先
級別 | 運算符 | 說明 |
---|---|---|
1 | =,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>= | 賦值、運算、賦值 |
2 | || | 邏輯或 |
3 | && | 邏輯與 |
4 | | | 按位或 |
5 | ^ | 按位異或 |
6 | & | 按位與 |
7 | ==,!= | 等於,不等於 |
8 | <=,>=,<,> | 小於等於,大於等於,小於,大於 |
9 | <<,>> | 按位左移,按位右移 |
10 | +,- | 加,減 |
11 | *,/,% | 乘,除,取模 |
12 | !,~ | 邏輯非,按位取反或補碼 |
13 | -,+ | 正,負 |
awk高級輸入輸出
讀取下一條記錄
awk中next
語句使用:在循環逐行匹配,如果遇到next,就會跳過當前行,直接忽略下面語句。而進行下一行匹配。next語句一般用於多行合並:
[root@ceshi awk]# cat ceshi
a
b
c
d
e
[root@ceshi awk]# awk ‘NR%2==1{next}{print NR,$0;}‘ ceshi
2 b
4 d
? 上例說明:當記錄行號除以2余1,就推過當前行。下面的print NR,$0
也不會執行。下一行開始,程序又開始判斷NR%2
值,這個時候記錄行號為2,就會執行下面語句塊:print NR,$0
分析發現需要將包含有“web”行進行跳過,然後需要將內容與下面行合並為一行:
[root@ceshi awk]# cat test2
web01[192.168.2.100]
httpd ok
tomcat ok
sendmail ok
web02[192.168.2.101]
httpd ok
postfix ok
web03[192.168.2.102]
mysqld ok
httpd ok
[root@ceshi awk]# awk ‘/^web/{T=$0;next;}{print T": "$0;}‘ test2
web01[192.168.2.100]: httpd ok
web01[192.168.2.100]: tomcat ok
web01[192.168.2.100]: sendmail ok
web02[192.168.2.101]: httpd ok
web02[192.168.2.101]: postfix ok
web03[192.168.2.102]: mysqld ok
web03[192.168.2.102]: httpd ok
簡單的讀取一條記錄
awk getline
用法:輸出重定向需用到getline函數
。getline從標準輸入、管道或者當前正在處理的文件之外的其他輸入文件獲得輸入。它負責從輸入獲得下一行的內容,並給NF NR和FNR等內建變量賦值。如果得到一條記錄,geline函數返回1,如果到達文件的末尾就返回0,如果出現錯誤,例如打開文件失敗,返回-1。
getline語法:getline var,變量var包含了特淡定行的內容。
awk getline整體用法說明:
- 當其左右無重定向符
|
或<
時:getline作用於當前文件,讀入當前文件的第一行給其後跟的變量var
或$0
(無變量),應該註意到,由於awk在處理getline之前已經讀入了一行,所以getline得到的返回結果是隔行的。 - 當其左右有重定向符
|
或<
時:getline作用於重定向輸入文件,由於該文件時剛打開,並沒有被awk讀入一行,只是getline讀入,那麽getline返回的是該文件的第一行,而不是隔行。
示例:
執行linux的date命令,並通過管道輸出給getline,然後再把輸出賦值給自定義變量out,並打印它:
[root@ceshi awk]# awk ‘BEGIN{ "date" | getline out;print out }‘
Tue Apr 17 14:47:31 CST 2018
執行shell的date命令,並通過管道輸出給getline,然後getline從管道中讀取並將輸入賦值給out,split函數把變量out轉化成數組mon,然後打印數組mon的第二個元素:
[root@ceshi awk]# date
Tue Apr 17 15:29:01 CST 2018
[root@ceshi awk]# awk ‘BEGIN{ "date" | getline out; split(out,mon); print mon[2] }‘
Apr
命令ls的輸出傳遞給geline作為輸入,循環使getline從ls的輸出中讀取一行,並把它打印到屏幕。這裏沒有輸入文件,因為BEGIN塊在打開輸入文件前執行,所有可以忽略輸入文件。
[root@ceshi awk]# ls
ceshi test test2
[root@ceshi awk]# awk ‘BEGIN{ while( "ls" | getline) print }‘
ceshi
test
test2
關閉文件
awk中允許在程序中關閉一個輸入或輸出文件,方法是使用awk的close語句。
close("filename")
? filename可以是getline打開的文件,也可以是stdin,包含文件名的變量或者getline使用的確切命令。或一個輸出文件,可以是stdout,包含文件名的變量或使用管道的確切命令。
輸出到一個文件
awk中允許用如下方式將結果輸出到一個文件:
[root@ceshi awk]# echo | awk ‘{printf ("hello world!\n") > "datafile"}‘
[root@ceshi awk]# cat datafile
hello world!
或
[root@ceshi awk]# echo | awk ‘{printf ("hello world!\n") >> "datafile2"}‘
[root@ceshi awk]# cat datafile2
hello world!
設置字段定界符
默認的字段定界符是空格,可以使用-F “定界符”
明確指定一個定界符:
awk -F: ‘{printf $NF}‘ /etc/passwd
或
awk ‘BEGIN{ FS=":"} {print $NF}‘ /etc/passwd
? 在BEGIN語句塊中則可以用OFS="定界符"
設置輸出字段的定界符。
流程控制語句
在linux awk的while、do-while、for語句中允許使用break、continue語句來控制流程走向,也允許使用exit這樣的語句來退出。break中斷當前正在執行的循環並調到循環外執行嚇一跳命令。if是流程選擇用法。awk中 流程控制語句,語法結構,與C語言類似。有了這些語句。其實很多shell程序都是可以交給awk,而且性能是非常快的。下面是各個語句用法。
條件判斷語句
if(表達式)
語句1
else
語句2
? 表達式中語句1 可以是多個語句,為了方便判斷和閱讀,最好將多個語句用{}括起來。awk分支結構允許嵌套,其格式為:
if(表達式)
{語句1}
else if(表達式)
{語句2}
else
{語句3}
示例:
#!/bin/bash
awk ‘BEGIN{
test=40;
if(test>90)
{print "very good!";}
else if(test>60)
{print "good!";}
else
{print "no pass";}
}
‘
運行輸出:
no pass
? 每條命令語句後面可以用;
分號結尾。
循環語句
while語句
while(表達式)
{語句}
示例:
awk ‘BEGIN{
test=5;
total=0;
while(i<test)
{
total+=i;
i++;
}
print "結果:"total;
}‘
運行結果
結果:10
i默認是0;此例為:計算0+1+2+3+4等於多少
for循環
格式1
for(變量 in 數組)
{語句}
示例:
awk ‘BEGIN{
for(k in ENVIRON){
print k"="ENVIRON[k];
}
}‘
TERM=linux
G_BROKEN_FILENAMES=1
SHLVL=1
pwd=/root/text
...
logname=root
HOME=/root
SSH_CLIENT=192.168.1.21 53087 22
? 註:ENVIRON是awk常量,是字典型數組。
格式2
for(變量;條件;表達式)
{語句}
示例:
awk ‘BEGIN{
total=0
for(i=0;i<5;i++)
{total+=i;}
print total
}‘
運行結果:
10
do循環
do
{語句} while(條件)
示例:
awk ‘BEGIN{
total=0;
i=0;
do {total+=i;i++;} while(i<5)
print total;
}‘
運行結果:
10
其他語句
- break:當break語句用於while或for語句時,導致退出程序循環。
- continue:當continue語句用於while或for語句時,使程序循環移動到下一個叠代。
- next:能夠導致讀入下一個輸入行,並返回到腳本的頂部 。這可以避免對當前輸入行執行其他的操作過程。
- exit:語句使主輸入循環退出 並將控制轉義到END,如果END存在的話,就執行END規則;如果沒有定義END規則,就在END中應用exit語句,終止腳本的執行。
數組應用
數組是awk的靈魂,處理文本中最不能少的就是它的數組處理。因為數組索引(下標)可以是數字和字符串,在awk數組中叫做關聯數組(associative arrays)。awk中的數組不必提前聲明,也不必聲明大小。數組元素用0或空字符串來初始化,這根據上下文而定。
數組的定義
數字做數組索引(也叫下標):
Array[1]="sun"
Array[2]="kai"
字符串做數組索引(下標):
Array["first"]="www"
Array["last"]="name"
Array["birth"]="1999"
使用print Array[1]會打印出sun;使用print Array["last"]會得到name。
讀取數組的值
{ for(item in array){print array[item]};} #輸出的順序是隨機的
{ for(i=1;i<=len;i++){print array[i]};} #len是數組的長度
數組相關函數
得到數組長度:
[root@ceshi awk]# awk ‘BEGIN{info="it is a test";lens=split(info,tA," ");print length(tA),lens;}‘
4 4
? length返回字符串以及數組長度,split進行分割字符串為數組,也會返回分割得到數組長度。
[root@ceshi awk]# awk ‘BEGIN{info="it is a test"; split(info,tA," "); print asort(tA);}‘4
? asort對數組進行排序,返回數組長度。
輸出數組內容(無序,有序輸出):
無序輸出:
[root@ceshi awk]# awk ‘BEGIN{info="it is a test"; split(info,tA," "); for(k in tA){print k,tA[k];}}‘
4 test
1 it
2 is
3 a
? for ... in
輸出,因為數組是關聯數組,默認是無序的。所以通過for ... in
得到是無序的數組。如果需要得到有序數組,需要通過下標獲得。
有序輸出:
方法1:
[root@ceshi awk]# awk ‘BEGIN{info="it is a test"; split(info,tA," "); for(k=1;k<=length(tA);k++){print k,tA[k];}}‘
1 it
2 is
3 a
4 test
方法2:
[root@ceshi awk]# awk ‘BEGIN{info="it is a test"; split(info,tA," "); for(k=1;k<=asort(tA);k++){print k,tA[k];}}‘
1 a
2 is
3 it
4 test
方法3:
[root@ceshi awk]# awk ‘BEGIN{info="it is a test"; Len=split(info,tA," "); for(k=1;k<=Len;k++){print k,tA[k];}}‘
1 it
2 is
3 a
4 test
? 註意:數組小標是從1開始,與C、python數組不一樣 小標從0開始。
判斷鍵值存在以及刪除鍵值:
#錯誤的判斷方法:
awk ‘BEGIN{tB["a"]="a1";tB["b"]="b1";if(tB["c"]!="1"){print "no found";};for(k in tB){print k,tB[k];}}‘
no found
a a1
b b1
c
? 以上出現奇怪問題,tB[“c”]
沒有定義,但是循環時候,發現已經存在該鍵值,它的值為空,這裏需要註意,awk數組是關聯數組,只要通過數組引用它的key,就會自動創建改序列。
#正確判斷方法:
[root@ceshi awk]# awk ‘BEGIN{tB["a"]="a1";tB["b"]="b1"; if("c" in tB){print "ok";}else{print "not in!"}; for(k in tB){print k,tB[k];}}‘
not in!
a a1
b b1
? if(key in array)通過這種方法判斷數組中是否包含key鍵值。
#刪除鍵值:
[root@ceshi awk]# awk ‘BEGIN{tB["a"]="a1";tB["b"]="b1"; delete tB["a"]; for(k in tB){print k,tB[k];}}‘
b b1
? delete array[key]可以刪除 對應數組key的序列值。
二維、多維數組使用
awk的多維數組在本質上是一維數組,更確切一點,awk在存儲上並不支持多維數組。awk提供了邏輯上模擬二維數組的訪問方式。例如:array[2,4]=1
這樣的訪問時允許的。awk使用一個特殊的字符串SUBSEP(?34)
作為分隔字段,在上面的例子中,關聯數組array存儲的鍵值實際上是2?344
。
類似一維數組的成員測試,多維數組可以使用if( (i,j) in array)
這樣的語法,但是下標必須放置在圓括號中。類似一維數組的循環訪問,多維數組使用for( item in array)
這樣的語法遍歷數組。與一維數組不同的是,多維數組必須使用split()
函數來訪問單獨的下標分量。
列出乘法口訣
#有序列出乘法口訣
awk ‘BEGIN{
for(i=1;i<=9;i++){
for(j=1;j<=9;j++){
tarr[i,j]=i*j;
print i,"*",j,"=",tarr[i,j];
}
}
}‘
運行結果:
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
......
9 * 9 = 81
? 可以通過array[k,k2]引用獲得數組內容。
另一種方法:
#無序列出乘法口訣
awk ‘BEGIN{
for(i=1;i<=9;i++){
for(j=1;j<=9;j++){
tarr[i,j]=i*j;
}
}
for(m in tarr){
split(m,tarr2,SUBSEP);
print tarr2[1],"*",tarr2[2],"=",tarr[m];
}
}‘
運行結果:
8 * 7 = 56
9 * 1 = 9
8 * 8 = 64
......
8 * 6 = 48
內置函數
awk內置函數,主要分以下幾種類:算術函數、字符串函數、其它一般函數、時間函數。
算術函數
格式 | 描述 |
---|---|
atan2()y,x | 返回y/x的反正切。 |
cos(x) | 返回x的余弦;x是弧度。 |
sin(x) | 返回x的正弦;x是弧度。 |
exp(x) | 返回x冪函數。 |
log(x) | 返回x的自然對數。 |
sqrt(x) | 返回x平方根。 |
int(x) | 返回x的截斷至整數的值。其實就是輸入小數返回一個整數。 |
rand( ) | 返回任意數字n,其中 0<=n<1。 |
srand([expr]) | 將rand函數的值設置為Expr參數的值,或如果省略Expr參數則使用某天的時間。返回先前的值。 |
舉例說明:
[root@ceshi awk]# awk ‘BEGIN{OFMT="%0.3f";fs=sin(1);fe=exp(10);fl=log(10);fi=int(3.1415);print fs,fe,fl,fi}‘
0.841 22026.466 2.303 3
OFMT 設置輸出數據格式是保留3位小數。
獲得隨機數:
[root@ceshi awk]# awk ‘BEGIN{srand();fr=int(100*rand());print fr;}‘
56
[root@ceshi awk]# awk ‘BEGIN{srand();fr=int(100*rand());print fr;}‘
97
[root@ceshi awk]# awk ‘BEGIN{srand();fr=int(100*rand());print fr;}‘
12
解讀:腳本執行過程:
- rand() 隨機返回一個六位小數點的小數,如:0.237788
- 然後是100rand(),就是100 0.237788 = 23.7788
- 通過int() 返回一個整數23。
- 再傳給srant() 作為參數,處理,輸出最後結果。
字符串函數
註:
Ere :可以是字符串、變量、也可以是正則表達式。
Repl:要替換成的值
In:是一個變量,或者字符串
格式 | 描述 |
---|---|
gsub(Ere,Repl,[ In ]) | 根據正則表達式替換所有匹配到的字符串,它和sub函數完全一樣地執行,但是sub只替換從左往右第一次匹配到的字符串。 |
sub(Ere,Repl,[ In ]) | 根據正則表達式Ere匹配In中所包含的匹配內容替換成Repl,並將替換後的內容賦值給In。sub函數返回匹配替換的數量。出現在Repl參數指定的字符串中的&(和符號) 由In參數指定的Ere參數的指定的擴展正則表達式匹配的字符串替換。如果未指定In參數,缺省值是整個記錄($0記錄變量)。 |
index(String1,String2) | 判斷string2是否出現在string1中,如果有返回位置,位置從1開始編號。如果String2參數不在String1參數中出現,則返回0。 |
length[(string)] | 返回string參數指定的字符串的長度(字符形式)。如果未給出string參數,則返回整個記錄的長度($0記錄變量)。 |
blength[(string)] | 返回string參數指定的字符串的長度(以字節為單位)。如果未給出string參數,則返回整個記錄的長度($0記錄變量)。 |
substr(string,M,[N]) | M:代表string裏邊的位置,M位置編號從1開始<br />N:代表要從M字符串的位置,往右取多少個值,包含M,但不包含M+N的和的值<br />命令意思:在string字符串中截取從 M位置開始取N個字符串,返回所取到的值。<br />如果未指定N參數,則代表截取從M位置到string參數的末尾位置,包含最後一個字符。 |
match(string,Ere) | 根據正則表達式Ere 在string中匹配查找,匹配到之後返回字符串所在位置,位置從1開始編號,或如果Ere參數不出現,則返回0。 RSTART特殊變量設置為返回值。RLENGTH特殊變量設置為匹配的字符串的長度,或如果未找到任何匹配,則設置為-1。 |
split(string,A,[Ere]) | 將string參數指定的參數分隔為數組元素A[1],A[2],...A[N],並返回n變量的值。此分隔可以通過Ere參數指定的擴展正則表達式進行,或用當前字段分隔符(FS特殊變量)來進行(如果沒有給出Ere參數)。除非上下文指明特定的元素還應具有一個數字值,否則A數組中的元素用字符串值來創建。 |
tolower(string) | 將string字符串裏邊的大寫字母都編程小寫字母。 |
toupper(string) | 將string字符串裏邊的小寫字母都變成大寫字母。 |
sprintf(Format,Expr,Expr,...) | 根據Format參數指定的printf子例程格式字符串來格式化Expr參數指定的表達式並返回最後生成的字符串。 |
gsub,sub使用
#gsub替換所有匹配的字符串
[root@ceshi awk]# awk ‘BEGIN{info="this is a test201test2010test!"; a=gsub(/[0-9]+/,"!",info); print a,info}‘
2 this is a test!test!test!
#sub只替換從左到右第一次匹配到的字符串
[root@ceshi awk]# awk ‘BEGIN{info="this is a test201test2010test!"; a=gsub(/[0-9]+/,"!",info); print a,info}‘
2 this is a test!test!test!
#當沒有匹配到的時候就返回原值。
[root@ceshi awk]# awk ‘BEGIN{info="this is a testtesttest!"; a=gsub(/[0-9]+/,"!",info); print a,info}‘
0 this is a testtesttest!
在info中查找滿足正則表達式 /[0-9]+/
用 " " 替換,並且替換後的值,賦值給info。如果沒有匹配到未給info賦值,輸出info的原值,默認替換數量為0。
查找字符串(index使用)
#查找如果test在info字符串中,就返回ok,不在返回no found
[root@ceshi awk]# awk ‘BEGIN{info="this is a test2010test2010test!"; print index(info,"test")?"ok":"no found!";}‘
ok
#查找如果test在info字符串中,返回test所在的位置,位置從左往右 編號從1開始。
[root@ceshi awk]# awk ‘BEGIN{info="this is a test2010test2010test!";b=index(info,"test");print b}‘
11
#匹配字符串,未找到返回0
[root@ceshi awk]# awk ‘BEGIN{info="this is a test2010test2010test!";b=index(info,"Test");print b}‘
0
正則表達式匹配查找(match使用)
#匹配字符串,匹配到返回ok
[root@ceshi awk]# awk ‘BEGIN{info="this is a test2010test2010test!";print match(info,/[0-9]+/)?"ok":"no found!";}‘
ok
#匹配到字符串,並返回字符串所在位置
[root@ceshi awk]# awk ‘BEGIN{info="this is a test2010test2010test!";a=match(info,/[0-9]+/); print a}‘
15
#匹配不到返回0
[root@ceshi awk]# awk ‘BEGIN{info="this is a testtesttest!";a=match(info,/[0-9]+/); print a}‘
0
截取字符串(substr使用)
#截取從M位置開始,向右取N個字符串
[root@ceshi awk]# awk ‘BEGIN{info="this is a test2010test!";a=substr(info,11,8);print a}‘
test2010
#不指定N值,就截取從M到string末尾的值
[root@ceshi awk]# awk ‘BEGIN{info="this is a test2010test!";print substr(info,11);}‘
test2010test!
字符串分隔(split使用)
[root@ceshi awk]# awk ‘BEGIN{info="this is a test!"; split(info,aa," "); print length(aa); for(k in aa){print k,aa[k];}}‘
4
4 test!
1 this
2 is
3 a
分隔字符串info,動態創建數組aa,用空格“ ”分隔符 風格info;
通過length獲取數組aa元素的總數,當然也可以直接split的時候賦值一個變量獲取元素的總數。
for(key in array)循環數組,是一個無序的循環。並不是從數組下標1...n,因此使用需要註意。
格式化字符串輸出(printf使用)
格式化字符串 格式:
格式化字符串包含兩部分內容:一部分是正常字符,這些字符將按原樣輸出;另一部分是格式化規定字符,以"%"開始,後跟一個或幾個規定字符,用來確定輸出內容格式。
格式 | 描述 |
---|---|
%d | 十進制有符號整數 |
%u | 十進制無符號整數 |
%f | 浮點數 |
%s | 字符串 |
%c | 單個字符 |
%p | 指針的值 |
%e | 指數形式的浮點數 |
%X | %X 無符號以十六進制表示的整數 |
%o | 無符號以八進制表示的整數 |
%g | 自動選擇合適的表示法 |
[root@ceshi awk]# awk ‘BEGIN{n1=124.113; n2=-1.224; n3=1.2345; printf("%.2f,%.2u,%.2g,%X,%o\n",n1,n2,n3,n1,n1);}‘
124.11,18446744073709551615,1.2,7C,174
一般函數
格式 | 描述 |
---|---|
close(Expression) | 用同一個字符串值Expression參數來關閉由 print或printf語句、打開或調用getline函數打開的文件或管道。如果文件或管道成功關閉,則返回0;其它情況下返回非0值。如果打算寫一個文件,並稍後在同一個程序中讀取文件,則close語句時必須的。 |
system(command) | 執行command 參數指定的命令,並返回退出狀態。等同於system子進程。 |
Expression | getline [ Variable ] | 從來自Expression參數指定的命令的輸出中 通過管道傳送的流中讀取一個輸入記錄,並將該記錄的值指定給Variable參數指定的變量。如果當前未打開將Expression參數的值作為其命令名稱的流,則創建流。創建的流等同於調用popen子進程,此時command參數取Expression參數的值且mode參數設置為一個是r的值。只要流保留打開且Expression參數求得同一個字符串,則對getline函數的每次後續調用讀取另一個記錄。如果未指定Variable參數,則$0記錄變量和NF特殊變量設置為從流讀取的記錄。 |
getline [ Variable ] < Expression | 從Expression參數指定的文件讀取輸入的下一個記錄,並將Variable參數指定的變量設置為該記錄的值。只要流保留打開且Expression參數對同一個字符串求值,則對getline函數的每次後續調用讀取另一個記錄。如果未指定Variable參數,則$0記錄變量和NF特殊變量設置為從流讀取的記錄。 |
getline [ Variable ] | 將Variable參數指定的變量設置為從當前輸入文件讀取的下一個輸入記錄。如果未指定Variable參數,則$0記錄變量設置為該記錄的值,還將設置NF、NR、FNR特殊變量。 |
打開外部文件(close用法)
[root@ceshi awk]# awk ‘BEGIN{ while("cat /etc/passwd" | getline){print $0;}; close("/etc/passwd");}‘
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
逐行讀取外部文件(getline使用方法)
[root@ceshi awk]# awk ‘BEGIN{ while(getline < "/etc/passwd"){print $0;};close("/etc/passwd");}‘
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@ceshi awk]# awk ‘BEGIN{print "Enter your name:";getline name;print name;}‘
Enter your name:
abc
abc
調用外部應用程序(system使用方法)
[root@ceshi awk]# awk ‘BEGIN{b=system("ls -al"); print b;}‘
total 24
drwxr-xr-x. 2 root root 77 Apr 17 15:58 .
dr-xr-x---. 12 root root 4096 Apr 12 15:01 ..
-rw-r--r--. 1 root root 10 Apr 17 13:23 ceshi
-rw-r--r--. 1 root root 13 Apr 17 15:39 datafile
-rw-r--r--. 1 root root 13 Apr 17 15:40 datafile2
-rw-r--r--. 1 root root 16 Apr 12 15:03 test
-rw-r--r--. 1 root root 222 Apr 17 14:25 test2
0
# b 代表返回值,是執行結果。0代表執行成功,非0表示執行失敗。
時間函數
格式 | 描述 |
---|---|
mktime( YYYY MM dd HH MM ss[DST]) | 生成時間格式 |
strftime([format [,timestamp]]) | 格式化時間輸出,將時間戳轉為時間字符串。具體格式見下表。 |
systime() | 得到時間戳,返回從1970年1月1日開始到當前時間(不計閏年)的整秒數 |
顯示一個時間(mktime使用)
[root@ceshi awk]# awk ‘BEGIN{tstamp=mktime("2018 04 19 10 27 30"); print strftime("%c",tstamp);}‘
Thu 19 Apr 2018 10:27:30 AM CST
%c 代表本地日期和時間格式。
#計算兩個時間 時間差,差的單位為秒
[root@ceshi awk]# awk ‘BEGIN{tstamp1=mktime("2018 04 19 10 0 0"); tstamp2=mktime("2018 04 19 11 0 0"); print tstamp2-tstamp1;}‘
3600
[root@ceshi awk]# awk ‘BEGIN{tstamp1=mktime("2001 01 01 0 0 0"); tstamp2=systime(); print "從2001年01月1日0點到現在時間相差秒:"tstamp2-tstamp1;print strftime("%c",tstamp2)}‘
從2001年01月1日0點到現在時間相差秒:545827039
Thu 19 Apr 2018 10:37:19 AM CST
strftime日期和時間格式說明符
格式 | 描述 |
---|---|
%a | 星期幾的縮寫(Sun) |
%A | 星期幾的完整寫法(Sunday) |
%b | 月名的縮寫(Oct) |
%B | 月名的完整寫法(October) |
%c | 本地日期和時間 |
%d | 十進制日期 |
%D | 日期 08/20/99 |
%e | 日期,如果只有一位會補上一個空格 |
%H | 用十進制表示24小時格式的小時 |
%I | 用十進制表示12小時格式的小時 |
%j | 從1月1日起一年中的第幾天 |
%m | 十進制表示的月份 |
%M | 十進制表示的分鐘 |
%p | 12小時表示法(AM/PM) |
%S | 十進制表示的秒 |
%U | 十進制表示的一年中的第幾個星期(星期天作為一個星期的開始) |
%w | 十進制表示的星期幾(星期天是0) |
%W | 十進制表示的一年中的第幾個星期(星期一作為一個星期的開始) |
%x | 重新設置本地日期(08/20/99)(月份/日期/年份) |
%X | 重新設置本地時間(12:00:00) |
%y | 兩位數字表示的年(99) |
%Y | 當前月份 |
%Z | 時區(PDT) |
%% | 百分號(%) |
第十九章 文本處理流編輯器:awk編程