第十九章 文本处理流编辑器:awk编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十九章 文本处理流编辑器:awk编程相关的知识,希望对你有一定的参考价值。

第十九章 文本处理流编辑器: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
A 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
line2 f2 f2
line3 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) 则是倒数第二个字段,其他依次类推:

[[email protected] awk]# echo -e "line1 f2 f3
line2 f4 f5" | awk ‘{print $NF}‘
f3
f5

[[email protected] awk]# echo -e "line1 f2 f3
line2 f4 f5" | awk ‘{print $(NF-1)}‘
f2
f4

打印每一行的第二和第三个字段:

[[email protected] awk]# echo -e "line1 f2 f3
line2 f4 f5" | awk ‘{print $2,$3}‘
f2 f3
f4 f5

统计文件中的行数:

[[email protected] awk]# echo -e "line1 f2 f3
line2 f4 f5" | awk ‘END{print NR}‘ 
2

以上命令只使用了END语句块,在读入每一行的时候,awk会将NR更新为对应的行号,当到达最后一行NR的值就是最后一行的行号,所以END语句块中的NR就是文件的行数。

计算每一行中的第一个字段累加的例子:

[[email protected] 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:

[[email protected] awk]# abc=2000
[[email protected] awk]# echo | awk -v def=$abc ‘{ print def }‘
2000

另一种传递外部变量方法:

[[email protected] 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也支持用逻辑运算符。

算术运算符

运算符 描述
+ - 加,减
* / & 乘,除,求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ -- 增加或减少,作为前缀或后缀

例:

[[email protected] awk]# awk ‘BEGIN{a="b"; print a++,++a;}‘
0 2

注意:所有用作算术运算符进行操作,操作数自动转为数值,所有非数值都变为0; 里边的++就等于 +1;a++就等于0+1。

再看下面的例子:

[[email protected] awk]# awk ‘BEGIN{a="b"; print a++,a,++a,++a;}‘
0 1 2 3

赋值运算符

运算符 描述
= 简单的赋值运算符
+= 加法赋值运算符
-= 减法赋值运算符
*= 乘法赋值运算符
/= 除法赋值运算符
%= 取模赋值运算符
^= 二进制位异或运算
**= 乘幂赋值运算符

例:

a+=5;等同于: a=a+5 ;其他运算符也同样

逻辑运算符

运算符 描述
|| 逻辑 或
&& 逻辑 与

例如:

[[email protected] awk]# awk ‘BEGIN{a=1;b=2;print (a>5 && b<=2),(a>5 || b<=2);}‘   
0 1
&&:代表两边条件必须都满足
||:达标两边条件一个满足即可

关系运算符

运算符 描述
< 小于
<= 小于等于
> 大于
>= 大于等于
!= 不等于
== 等于

例如:

[[email protected] awk]# awk ‘BEGIN{a=11; if(a>=9){print "ok";}}‘
ok

注意:> < 可以作为字符串比较,也可以作为数值比较,关键看操作符。如果是字符串会转换为字符串比较。两个都为数字才转为数值比较。字符串比较:是按照ASCII码的对应的顺序比较。

其它运算符

运算符 描述
$ 字段引用
空格 字符串连接符
?: C条件表达式
in 数组中是否存在某键值
not in 数组中不存在某键值

例如:

[[email protected] awk]# awk ‘BEGIN{ print 5==5?"ok":"err";}‘                
ok
[[email protected] awk]# awk ‘BEGIN{ print 5==6?"ok":"err";}‘                
err
[[email protected] awk]# awk ‘BEGIN{a="b"; arr[0]="b"; arr[b]="c"; print (a in arr);}‘
0
[[email protected] 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语句一般用于多行合并:

[[email protected] awk]# cat ceshi 
a
b
c
d
e
[[email protected] 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”行进行跳过,然后需要将内容与下面行合并为一行:

[[email protected] 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

[[email protected] 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,并打印它:

[[email protected] 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的第二个元素:

[[email protected] awk]# date
Tue Apr 17 15:29:01 CST 2018
[[email protected] awk]# awk ‘BEGIN{ "date" | getline out; split(out,mon); print mon[2] }‘  
Apr

命令ls的输出传递给geline作为输入,循环使getline从ls的输出中读取一行,并把它打印到屏幕。这里没有输入文件,因为BEGIN块在打开输入文件前执行,所有可以忽略输入文件。

[[email protected] awk]# ls    
ceshi  test  test2
[[email protected] awk]# awk ‘BEGIN{ while( "ls" | getline) print }‘
ceshi
test
test2

关闭文件

awk中允许在程序中关闭一个输入或输出文件,方法是使用awk的close语句。

close("filename")

? filename可以是getline打开的文件,也可以是stdin,包含文件名的变量或者getline使用的确切命令。或一个输出文件,可以是stdout,包含文件名的变量或使用管道的确切命令。

输出到一个文件

awk中允许用如下方式将结果输出到一个文件:

[[email protected] awk]# echo | awk ‘{printf ("hello world!
") > "datafile"}‘
[[email protected] awk]# cat datafile 
hello world!
或
[[email protected] awk]# echo | awk ‘{printf ("hello world!
") >> "datafile2"}‘
[[email protected] 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是数组的长度

数组相关函数

得到数组长度

[[email protected] awk]# awk ‘BEGIN{info="it is a test";lens=split(info,tA," ");print length(tA),lens;}‘
4 4

? length返回字符串以及数组长度,split进行分割字符串为数组,也会返回分割得到数组长度。

[[email protected] awk]# awk ‘BEGIN{info="it is a test"; split(info,tA," "); print asort(tA);}‘4

? asort对数组进行排序,返回数组长度。

输出数组内容(无序,有序输出):

无序输出:
[[email protected] 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:
[[email protected] 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:
[[email protected] 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:
[[email protected] 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,就会自动创建改序列。

#正确判断方法:
[[email protected] 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键值。

#删除键值:
[[email protected] 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参数则使用某天的时间。返回先前的值。

举例说明:

[[email protected] 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位小数。

获得随机数:

[[email protected] awk]# awk ‘BEGIN{srand();fr=int(100*rand());print fr;}‘
56
[[email protected] awk]# awk ‘BEGIN{srand();fr=int(100*rand());print fr;}‘
97
[[email protected] 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替换所有匹配的字符串
[[email protected] awk]# awk ‘BEGIN{info="this is a test201test2010test!"; a=gsub(/[0-9]+/,"!",info); print a,info}‘
2 this is a test!test!test!

#sub只替换从左到右第一次匹配到的字符串
[[email protected] awk]# awk ‘BEGIN{info="this is a test201test2010test!"; a=gsub(/[0-9]+/,"!",info); print a,info}‘
2 this is a test!test!test!

#当没有匹配到的时候就返回原值。
[[email protected] 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
[[email protected] awk]# awk ‘BEGIN{info="this is a test2010test2010test!"; print index(info,"test")?"ok":"no found!";}‘                              
ok

#查找如果test在info字符串中,返回test所在的位置,位置从左往右 编号从1开始。
[[email protected] awk]# awk ‘BEGIN{info="this is a test2010test2010test!";b=index(info,"test");print b}‘                
11

#匹配字符串,未找到返回0
[[email protected] awk]# awk ‘BEGIN{info="this is a test2010test2010test!";b=index(info,"Test");print b}‘ 
0

正则表达式匹配查找(match使用)

#匹配字符串,匹配到返回ok
[[email protected] awk]# awk ‘BEGIN{info="this is a test2010test2010test!";print match(info,/[0-9]+/)?"ok":"no found!";}‘
ok

#匹配到字符串,并返回字符串所在位置
[[email protected] awk]# awk ‘BEGIN{info="this is a test2010test2010test!";a=match(info,/[0-9]+/); print a}‘             
15

#匹配不到返回0
[[email protected] awk]# awk ‘BEGIN{info="this is a testtesttest!";a=match(info,/[0-9]+/); print a}‘ 
0

截取字符串(substr使用)

#截取从M位置开始,向右取N个字符串
[[email protected] awk]# awk ‘BEGIN{info="this is a test2010test!";a=substr(info,11,8);print a}‘
test2010

#不指定N值,就截取从M到string末尾的值
[[email protected] awk]# awk ‘BEGIN{info="this is a test2010test!";print substr(info,11);}‘     
test2010test!

字符串分隔(split使用)

[[email protected] 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 自动选择合适的表示法
[[email protected] awk]# awk ‘BEGIN{n1=124.113; n2=-1.224; n3=1.2345; printf("%.2f,%.2u,%.2g,%X,%o
",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用法)

[[email protected] 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使用方法)

[[email protected] 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
[[email protected] awk]# awk ‘BEGIN{print "Enter your name:";getline name;print name;}‘
Enter your name:
abc
abc

调用外部应用程序(system使用方法)

[[email protected] 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使用)

[[email protected] 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 代表本地日期和时间格式。
#计算两个时间 时间差,差的单位为秒
[[email protected] awk]# awk ‘BEGIN{tstamp1=mktime("2018 04 19 10 0 0"); tstamp2=mktime("2018 04 19 11 0 0"); print tstamp2-tstamp1;}‘      
3600
[[email protected] 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编程的主要内容,如果未能解决你的问题,请参考以下文章

从零开始的linux 第十九章(mv命令详解)

C#图解教程 第十九章 LINQ

WPF学习第十九章 控件类

第十九章-组合模式

第十九章枚举集合

第十九章 Django框架——Admin组件