# 前言

关于 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...

变量 RSORS 定义了 gawk 程序如何处理数据流中的记录,默认下,都为换行符,默认的 RS 值表明,
输入数据流中的每行新文本就是一条新纪录。

你如果不是很清楚,可以参照这篇文章:RS、FS 与 ORS、OFS 等内置变量的用法

# 数据变量

gawk 还提供了一些内建变量帮助你了解数据发生了什么变化,并提取 shell 环境的变化,太多了,挑几个写。

ARGCARGV 变量允许从 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 大同小异, BEGINEND 关键字用在读取数据流之前 / 后执行命令的特殊模式,也存在其他特殊模式,用于在匹配数据流时执行一些命令。

# 正则表达式

正则表达式必须出现在要控制的程序脚本的左花括号之前。

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 转换为格式化日期 (采用 shelldate() )
    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 吧?)学习的路上,我们都还差得远呢,剩下的,也是唯一需要记住的就是,保持学习,尊重原创。