# 前言
关于 gawk
进阶,你可以参考这篇博客,很详细,但是请一定要看本文的结束语
gawk 进阶。
# 内建变量
gawk
程序使用内建变量来引用程序的数据里的一些特殊功能。
# 字段和记录分隔符的变量
类似于 $1,$2
是数据字段变量,其根据字段分隔符来划定,默认情况下,字段分隔符是空白字符 (空格或制表符)。可以使用内建变量 FS
更改字段分隔符( shell
脚本里面用的是 IFS
)
FIELDWIDTHS--由空格分隔的一列数字,定义了每个数据字段确切宽度 | |
FS--输入字段之间分隔符 | |
OFS--输出字段之间分隔符 | |
RS--输入记录分隔符 | |
ORS--输出记录分隔符 |
默认情况下, OFS
是空格,所以 print $1,$2,$3 --> field1 field2 field3
。
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{OFS="-"} {print $1,$2,$3}' data | |
dog-monkey-cat | |
cyan@cyan-virtual-machine:~/Templates$ cat data | |
dog monkey cat mouse | |
# 在 print 中,使用,隔开每个变量,同时 FS="," 要使用双引号 |
设置 FIELDWIDTHS
时,该变量允许你不依靠字段分隔符来读取记录,一旦设置, gawk
就会忽略 FS
,并根据提供的字段宽度来计算字段。
gawk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data | |
# 那么字段 $1 长度为 3,$2 长度为 5... |
变量 RS
和 ORS
定义了 gawk
程序如何处理数据流中的记录,默认下,都为换行符,默认的 RS
值表明,
输入数据流中的每行新文本就是一条新纪录。
你如果不是很清楚,可以参照这篇文章:RS、FS 与 ORS、OFS 等内置变量的用法。
# 数据变量
gawk
还提供了一些内建变量帮助你了解数据发生了什么变化,并提取 shell
环境的变化,太多了,挑几个写。
ARGC
和 ARGV
变量允许从 shell
中获取命令行参数的总数以及它们的值, gawk
并不会将程序脚本当成命令行参数的一部分。
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{print ARGC,ARGV[1]}' data | |
2 data | |
# ARGC 表示参数为两个,包括 gawk 命令和 data 参数 (程序脚本不是参数!) |
ENVIRON
-- 使用关联数组来提取 shell
环境变量,该数组用文本作为数组的引索值,而不是数值。
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{print ENVIRON["HOME"],ENVIRON["PATH"]}' | |
/home/cyan /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin |
# 自定义变量
gawk
自定义变量不能以数字开头,直接赋值即可,不需要使用 $
,同时, gawk
包含了标准算数操作符 (*,^,**,%...)
。
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{x=4; x= x ** 3 + 1; print x}' | |
65 | |
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{x=4; x= x ^ 3 + 1; print x}' | |
65 |
# 数组
gawk
使用关联数组提供数组功能,不同之处在于,其引索值可以是任意文本字符串,每个引索字符串都必须能够唯一标识出赋给他的数据元素。每个数组名自定义,数据引索值自定义!
基本定义格式: arr[index_1]=val_1
。
1.遍历数组变量: | |
for(var in array) | |
{ | |
statements | |
} | |
2.删除数组变量: | |
delete array[index] |
# 使用模式
gawk
支持多种类型的匹配模式来过滤数据,与 sed
大同小异, BEGIN
和 END
关键字用在读取数据流之前 / 后执行命令的特殊模式,也存在其他特殊模式,用于在匹配数据流时执行一些命令。
# 正则表达式
正则表达式必须出现在要控制的程序脚本的左花括号之前。
gawk 'BEGIN{FS=","} /11/{print $1}' data |
# 匹配操作符 (matching operator)
匹配操作符是波浪线 ~
,允许将正则表达式限定在记录中的特定数据字段。
正则表达式有一定局限性,因为我们现在处理的数据,从行记录数据细化到了某个字段的数据(通过 FS
字段分隔符细化),所以,比如我想匹配每行中,第五个字段中是否有 cat
这个单词,单使用正则表达式是做不到的。
$5 ~ /cat/ | |
# $5 表示行记录数据中第五个字段,该表达式会过滤出第五个字段包含 cat 的所有记录 | |
# 这是一个强大的功具 -- 用于在数据文件中搜索特定的数据元素 |
它还可以和 !
一起使用,用于排除,没错,和 sed
一样的功能
$1 !~ /expression/ | |
# 如果匹配失败就会执行指令 |
# 数学表达式
gawk -F: '$4 == 0{print $1}' /etc/passwd | |
# 该脚本查看第四个数据字段含有 0 的值 (组 ID=0--root 用户组) |
常见的比较: >= , <= , == ...
,也可以对文本数据使用表达式: gawk '$1 == "data"{print $2}' data
。
# 结构化语句
# if 语句
if (condition) | |
statement1 | |
# 或者是:if (condition) statement |
如果要在 if
语句中执行多条语句,要使用花括号,当然, gawk
也支持 else
子句。
if (condition) | |
{ | |
statements | |
} else | |
{ | |
} | |
# 也可以在单行使用 else,只是需要加;if (condition) statements; else statements |
# while 语句
while (condition) | |
{ | |
statements | |
# while 中支持 break 和 continue 来跳出循环 | |
} |
还有 do-while
语句:
do | |
{ | |
statements | |
}while (condition) |
# for 语句
for( var assignment; condition; iteration process) | |
# 其实跟普通的 for 循环没啥区别 | |
# 大概就是这样 | |
> for(i=1;i<4;i++) | |
> { | |
> total += $i | |
> } |
最后还是举个例子
cyan@cyan-virtual-machine:~/Templates$ cat commands.awk | |
{ | |
total=0 | |
for(i=0; i<3; i++) | |
{ | |
print $1 | |
} | |
} | |
cyan@cyan-virtual-machine:~/Templates$ gawk -f commands.awk data | |
dog | |
dog | |
dog |
# 格式化打印
和 c
语言的 printf
差不多,通过格式化字符来控制。
d/i--都是表示整数 | |
f--浮点数 | |
e--科学计数法 | |
g--科学计数法表示,或浮点数 | |
o--八进制 | |
x--十六进制 | |
X--十六进制,字母大写 | |
printf "The answer is:%e",val |
width
-- 指定输出字段最小宽度的数字值,如果输出小于这个值,文本右对齐,并用空格填充,否则按实际宽度输出。prec
-- 指定了浮点数中小数点后面的位数,或者文本字符串中显示的最大字符数。-
(减号):指明在向格式化空间中放入数据时采取左对齐而不是右对齐。
通过一个例子来讲解,FS 是一行数据中各个字段的分隔符,RS 是行数据之间的分隔符。
# 数据文件 | |
cyan@cyan-virtual-machine:~/Templates$ cat data | |
Riley Mullen,(312)555-1234 | |
Frank Williams,(317)555-9876 | |
Haley Snell,(313)555-4938 | |
# 先试一下 printf 打印 | |
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{FS=",";RS="\n"} {printf "%s %s\n",$1,$2}' data | |
Riley Mullen (312)555-1234 | |
Frank Williams (317)555-9876 | |
Haley Snell (313)555-4938 |
我们希望打印结果对齐一点,但是默认的是右对齐
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{FS=",";RS="\n"} {printf "%20s %s\n",$1,$2}' data | |
Riley Mullen (312)555-1234 | |
Frank Williams (317)555-9876 | |
Haley Snell (313)555-4938 |
所以要使用 -
,指定左对齐,完美。
cyan@cyan-virtual-machine:~/Templates$ gawk 'BEGIN{FS=",";RS="\n"} {printf "%-20s %s\n",$1,$2}' data | |
Riley Mullen (312)555-1234 | |
Frank Williams (317)555-9876 | |
Haley Snell (313)555-4938 |
# 内建函数
# 数学函数
atan2(x,y)--x/y反正切,x和y是弧度 | |
cos(x),log(x),sin(x),sqrt(x),srand(x)为计算机随机数指定种子值 | |
rand()--产生0~1之间的随机小数(不含0,1) | |
exp(x)--x的指数函数 | |
int(x)--取整 | |
除了一般的数学运算外,gawk还提供位运算: | |
and(x,y),compl(x)--x的补运算 | |
lshift(x,count)--左移,rshift(x,count)--右移 | |
or(x,y),xor(x,y) |
# 字符串函数
具体详细的,要使用时,自己去查
length([s])--长度 | |
index(s,t) |
# 时间函数
mktime(datespec)
按YYYY-MM-DD-HH-MM-SS[DST]
格式指定的日期转换成时间戳值。strftime(formate [,timestamp])
将当前时间的时间戳或timestamp
转换为格式化日期 (采用shell
的date()
)systime()
返回当前时间的时间戳
cyan@cyan-virtual-machine:~$ gawk '{ | |
date=systime() | |
date=strftime("%A, %B %d, %Y",date) | |
print date | |
}' |
# 自定义函数
function name([varuables]) #可以传参 | |
{ | |
statements | |
return value #可以使用 return 返回值 | |
} | |
#在自定义函数时,必须出现在所有代码前 (包括 BEGIN 代码块),有助于将函数代码和 gawk 程序其他部分分开 | |
gawk ' | |
> function fun() | |
> { | |
> printf "%-16s - %s\n",$1,$4 | |
> } | |
> BEGIN{FS="\n"; RS=""} | |
> { | |
> fun() | |
> }' data |
# 创建函数库
如果函数经常使用,可以将这些函数放在专门的函数库文件中,文件可以通过 -f
参数来使用。但是, -f
命令行参数不能和内联 gawk
脚本放到一起使用,所以在同一行命令行需要使用多个 -f
参数 (也就是,将 gawk
脚本程序也放到文件中)。
gawk -f funclib -f script data |
# 结束语
至此,暑假开的 shell
系列更新结束(2022.7.28--20228.6),所有文章大致参考《Linux 命令行与 shell 脚本编程大全》,作者是 [美] 布鲁姆(Richard Blum),布雷斯纳汉(Christine Bresnahan)。为什么说大致呢?首先,这本书是我在大二寒假看完的,当时笔记做的不是很完善而且还有很多错误,所以这次博客更新,我并不只是单纯从之前的笔记粘贴复制。你们现在看到的,很多例子,都是我自己想的,问题的引入和思考花费了较多时间,但是绝对不能否认的是,这本书让我收获极大。
本文开始推荐的博客,其实就是《Linux 命令行与 shell 脚本编程大全》的 gawk 进阶的原文(作者在文末也说了参考了此书),我并不喜欢照着书抄笔记,一个是我懒,另一个是这样我还不如让你直接买一本。
送君千里,终有一别。在 CUI(不会到现在你还不知道什么是 CUI 吧?)学习的路上,我们都还差得远呢,剩下的,也是唯一需要记住的就是,保持学习,尊重原创。