1. 程式人生 > >第十九章 文本處理流編輯器:awk編程

第十九章 文本處理流編輯器:awk編程

並不是 文本 取模 用戶自定義函數 taf linu man align 邏輯運算

第十九章 文本處理流編輯器: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 }‘
  1. 第一步:執行BEGIN{ commands }語句塊中的語句。
  2. 第二步:從文件或標準輸入(stdin)讀取一行,然後執行pattern{ commands }語句塊,它逐行描述文件,從第一行到最後一行重復這個過程,知道文件全部被讀取完畢。
  3. 第三步:當讀至輸入流末尾時,執行END{ 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整體用法說明:

  • 當其左右重定向符|&lt;時:getline作用於當前文件,讀入當前文件的第一行給其後跟的變量var$0(無變量),應該註意到,由於awk在處理getline之前已經讀入了一行,所以getline得到的返回結果是隔行的。
  • 當其左右重定向符|&lt;時: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

解讀:腳本執行過程:

  1. rand() 隨機返回一個六位小數點的小數,如:0.237788
  2. 然後是100rand(),就是100 0.237788 = 23.7788
  3. 通過int() 返回一個整數23。
  4. 再傳給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編程