Linux-gawk命令
背景
此前讲到sed
命令,它是流编辑方式的一种,但是由于其自身的限制,导致其无法很好的组织文件中的数据。这一点就被gawk给解决了,gawk
提供了类编程环境的方式去处理文本:支持变量、提供算数计算、结构化编程、数据提取
,所以gawk
要强大很多。
内容
入门
gawk命令的格式如下:
1 | options是gawk的参数 |
其中options
部分常用的参数如下(摘录自《Linux命令行与Shell编程大全》):
1 | -F fs:指定行中划分数据字段的字段分隔符 |
program
是处理文本的指令,首先讲到打印命令print
:
1 | 它会在STDOUT中输出对应的字符 |
你会发现上面的内容还是有点特点的,这个就要说到gawk
中program
的格式:
1 | 在命令行中,以引号包裹全部内容,然后用{}包裹program命令,多条命令之间用;进行间隔 |
gawk的一大特性是会自动的给一行的每个数据元素分配一个变量,这些变量可以通过$1~$n
依次获取,其中$0
表示的是该文本行的所有数据。比如:
1 | gawk '{print $0}' /etc/passwd |
如果需要修改某个数据,则可以通过$num=valiable
进行修改,然后再将修改后的打印出来
1 | gawk '{$4="hahahah";print $0}' file |
如果将program
独立成单独的文件来供调用,则可以借助参数:-f
1 | gawk -f script.gawk file |
gawk
还支持在处理数据前后运行指定脚本用于输出提示内容,这个需要借助于参数:BEGIN
、END
1 | BEGIN:数据处理前执行的脚本 |
中级
gawk
内置一整套类编程语言处理机制,而作为一款语言,它的一大重要特性就是变量。gawk
中支持两种类型的变量:内建变量、自定义变量
,其中内建变量之前我们接触过:
1 | $1~$n:标识了文本行中每个文本数据的位置 |
每一行的单个文本数据之间的间隔标识默认为空格
/制表符
,当然也可以在使用命令的时候就通过参数-F fs
来指定,比如;
1 | 以:作为文本数据之间的间隔符 |
但是在命令行直接通过-F
的方式,其作用于整个program
运行期间,不是很明智,其实我们可以在program
中通过命令:FS
指定输入字段分隔符,比如:
1 | FS也是gawk的内建变量之一 |
此外还有输出字段分隔符:OFS
,它的作用与FS
类似,只不过用在print
上。
1 | 默认情况下,gawk将OFS设置为空格,OFS的字符用于拼接print输出的每个字段 |
有别于FS
设置间隔符的方式,gawk
还提供了设置定长格式内建变量:FIELDWIDTHS
,专门用于根据字段宽度来分割文本行数据的:
1 | 若一行分割后仍有剩余,则直接丢弃了 |
然而一旦设置了FIELDWIDTHS
,则不能够再改变,所起其不适用长度变化的字符串。
默认情况下,gawk
是读取一行处理一行数据的,但是也可以借助输入记录分隔符:RS
,一次性读取到文本的指定位置后再进行处理,比如:
1 | gawk将间隔空行后的每个内容部分当作一个文本处理对象,同时会将每个文本处理对象中以换行符间隔的内容当作一个文本数据。 |
与RS
相类似的还有输出记录分割符:ORS
,只不过它作用的是print
,默认情况下,gawk
给RS
和ORS
设置初始值为\n
。比如:
1 | gawk 'BEGIN {FS="\n";RS="";ORS="\n\n";OFS=" | "}; {print $1,$2,$3,$4}' file |
除了上述描述的内建变量外,gawk
还提供了其它的内建变量,常用如下:
变量 | 描述 |
---|---|
ARGC | 获取命令行参数个数 |
ARGIND | 当前文件在ARGV中的位置 |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字的转换格式。默认为%.6g |
ENVIRON | 当前shell环境变量及其值组成的关联数组 |
ERRNO | 当读取、关闭输入文件发生错误时的系统错误号 |
FILENAME | 用作gawk输入数据的数据文件的文件名 |
FNR | 当前数据文件中的数据行数 |
IGNORECASE | 设置非0值,忽略gawk 命令中出现的字符串的字符大小写 |
NF | 读取的数据文本中的字段总数 |
NR | 已处理的输入记录数 |
OFMT | 数字的输出格式,默认%.6g |
RLENGTH | 由match函数匹配的子字符串的长度 |
RSTART | 由match函数匹配的子字符串的起始位置 |
上面摘录自《Linux命令行与Shell编程大全》的参数看着纷繁复杂,但是文中针对以下几个常用的做了描述:
ARGC
和ARGV
1
2
3
4ARGC:获取命令行传递过来的参数总个数,但是将脚本部分(program)不计算在内
ARGV:获取指定参数位置的值,格式 --> ARGV[num],num从0开始计数
gawk 'BEGIN {print ARGC, ARGV[1]}' fileENVIRON
1
2
3
4
5
6ENVIRON:从gawk中直接获取Shell的环境变量
格式:
ENVIRON["环境变量名"] file
比如
gawk 'BEGIN {print ENVIRON["HOME"]}' fileNF
1
2NF:获取数据文本的总个数,是数字
gawk 'BEGIN {print $NF}' fileNR
和FNR
1
2
3
4
5NR:记录已经处理的文本块数,文本处理对象变换后,也不会重置
gawk '{print "Count="NR}' file
FNR:记录当前文本处理对象的处理个数, 当文本处理对象变换后,FNR重置从1开始
gawk '{print "Count="FNR}' file
上面描述的是gawk
的内建变量,对于用户自己定义的变量,则可以直接通过如下方式:
1 | gawk变量名支持字母、数字、下划线,但不允许数字开头,数据有两种类型:数字、字符 |
你应该也注意到了,在gawk
中,变量的引用都是直接使用其名字的,不需要加$
符号。此外,gawk
还支持数学运算,如下:
1 | 除了普通的四则运算外,还支持%(取余)、**(幂运算)、^(幂运算) |
gawk
中还提供了数组类型的数据,于其说它是数组,起始更像是Python中的字典,索引必须唯一,定义格式如下:
1 | index可以是字符、字符串、数字 |
gawk
为了遍历访问数据元素,还提供了for循环
,也极其的像Python中的:
1 | 切记,此处的var记录的是name的索引值 |
而对于gawk
中用不到的数组元素,也可以通过:delete
删除:
1 | 格式; |
高级
上面的都没有描述过区间筛选的内容,当我们需要定位指定的位置去操作指令的时候,这个时候就要讲到匹配模式
:
1 | 匹配模式的内容一定要位于{}的左花括号外 |
匹配模式
同样支持正则:
1 | gawk '/.d/{print $1}' file |
然而上面的内容,针对的是一个处理单元,匹配模式
还支持针对处理单元中的单个数据文本进行定位匹配处理,这个就需要借助命令:~
1 | $num外面没有{}包裹 |
同样,也可以增加!
来表示对正则取反
1 | gawk会取第二个数据文本来匹配正则,不符合正则的元素都会被执行command |
此外匹配模式
下还支持数值比较:
1 | 比较的字符:==、>=、<=、>、<、!= |
也支持字符串比较,不过只有一个比较符:==
1 | ==:必须完全匹配 |
gawk
支持标准的if -- else
语句:
1 | 单个if |
比如:
1 | gawk '{ if ($2 < 10) |
同样,gawk支持while
、do -- while
、for
三种循环:
while循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14格式
while (condition)
{
commands
}
比如求出每一列的数据单个数据和
total=0
i=1
while (i <= NF)
{
total+=$i # 看见木有,都支持这种表达式了
i++
}do -- while
循环1
2
3
4
5
6
7
8
9
10
11
12
13
14和while循环不一样,do -- while循环是先执行循环体
do
{
commands
} while (condition)
比如
total=0
i=1
do
{
total += $i
i++
} while (i < NF)for
循环1
2
3
4
5
6
7
8
9
10
11
12格式
for (var; condition; iteration)
{
command
}
比如
total=0
for(i=1;i<=NF;i++)
{
total += $i
}
此外,gawk
还支持跳出循环的break、continue
1 | for(i=1;i<=NF;i++) |
gawk
的格式化输出中还有一个与print
类似的命令:printf
,它具有高度的可定制化输出的功能。
1 | 和c语言的printf简直神似,格式 |
但是有一点和print
不大一样的是,printf
默认是不会输出换行符的,所以需要用户自己在对应的位置加上换行符,否则输出内容将全部打印到一行中:
1 | gawk '{printf "wudashuai zeishuai\n"}' file |
printf
中的format string
支持格式化输出:
1 | 格式: |
其中control-letter
支持以下参数:
1 | c:将一个数当作ASCII值显示,这个我没懂 |
width
用于控制输出字段的最小宽度,大于这个宽度则原长度输出,反之则以空格补充。而prec
用于控制显示小数点后的位数,或者是文本字符串中的最大字符数。
1 | 对于浮点数 |
默认情况下,输出内容在输出的区间内是右对齐的,如果需要左对齐,则需要借助:-
1 | gawk 'BEGIN {c=1.808; printf "%-10.5f\n",c}' file |
此外,gawk
还提供了很多的内建函数用于数据处理,以下就直接摘录《LInux命令行和Shell编程大全》:
对于数学运算,gawk提供如下函数:
函数 | 描述 | 举例 |
---|---|---|
atan2(x, y) | 求x/y的反正切,x和y以弧度为单位 | gawk 'BEGIN {x=atan2(2,3); printf "%2.4f", x}' file |
sin(x) | x的正弦值,x以弧度为单位 | gawk 'BEGIN {x=sin(2); printf "%2.4f", x}' file |
cos(x) | x的余弦值,x以弧度为单位 | gawk 'BEGIN {x=cos(2); printf "%2.4f", x}' file |
exp(x) | x的指数函数, x=exp(100)表示求x的100次方值 |
gawk 'BEGIN {x=exp(2); printf "%2.4f", x}' file |
log(x) | 求x的自然对数 | gawk 'BEGIN {x=log(2); printf "%2.4f", x}' file |
sqrt(x) | x的平方根 | gawk 'BEGIN {x=sqrt(2); printf "%2.4f", x}' file |
int(x) | 取x靠近0一侧的整数值 | gawk 'BEGIN {x=int(2.1); printf "%2.4f", x}' file |
rand() | 取0~1之间的随机数值 | gawk 'BEGIN {x=rand(); printf "%.4f", x}' file |
不过gawk
的算数运算也有最大的上限值,如果超过该值,则gawk
报错。
对于位运算,gawk
提供如下函数(都是操作值对应的二进制位):
函数 | 描述 | 举例 |
---|---|---|
and(x,y) | 执行x和y的按位与运算 |
gawk 'BEGIN {x=and(1,2); printf "%2.4f", x}' file |
or(x,y) | 执行x和y的按位或运算 |
gawk 'BEGIN {x=or(1,2); printf "%2.4f", x}' file |
xor(x,y) | 执行x和y的按位异或运算 |
gawk 'BEGIN {x=xor(1,2); printf "%2.4f", x}' file |
compl(x) | 执行x的补运算 ,这个不大懂 |
gawk 'BEGIN {x=compl(2); printf "%2.4f", x}' file |
lshift(var, count) | 将值var左移count位 | gawk 'BEGIN {x=lshift(2,1); printf "%2.4f", x}' file |
rshift(var, count) | 将值var右移count位 | gawk 'BEGIN {x=rshift(2,1); printf "%2.4f", x}' file |
对有字符串处理,gawk提供如下函数:
asort(s [,d])
将
数组s
按数据元索值排序,索引值会被替换成表示新的排序顺序的连续数字,若指定了d
,则排序后的数组存储在数组d
1
2
3
4
5gawk 'BEGIN {asort(list, d);
for (var in d)
{
printf d[var]
}}' fileasorti(s [,d])
将
数组S
按索引值排序,生成的数组会将索引值作为数据元索值,用连续数字索引来表明排序顺序,若指定了d
,排序后的数组会存储在数组d
中1
2
3
4
5gawk 'BEGIN {asorti(list, d);
for (var in d)
{
printf d[var]
}}' filegensub(r, s, h [, t])
查找
变量$0
,或者目标字符串t
来匹配正则表达式r
。如果h
是以g/G
开头的字符串,则用s
替代匹配的文本。如果h
是一个数字,它表示要替换掉第h处r
匹配的地方。它不直接修改读取的数据,但会返回替换后的数据。
默认会将匹配的每一处正则都替换为指定的数据。
1
2
3
4gawk '/BUGS/{
data=gensub("BUGS", "wuxiang", "2", $1) --> 处理每个文本单元中的第一个数据文本
print data
}' out.txtgsub(r,s [,t])
查找
变量$0
,或者目标字符串t
来匹配正则表达式r
。如果找到了,就全部替换成字符串s
。它会直接修改读取的数据,默认替换所有匹配处的内容,并且会返回
字符串t
中匹配正则r
的个数。1
2
3
4gawk '{
count=gsub("log", "wuxiang")
printf "%2d %s\n", count, $0
}' out.txtindex(s,t)
返回
字符串t
在字符串s
中的索引位置(是字符串数目的位置,不是数据文本的位置),如果没找到则返回0
1
2
3
4
5
6gawk '{
count=index($0, "log")
if (count != 0) {
print count
}
}' out.txtlength([s])
返回
字符串s
的长度;若没有指定s
,则返回$0
的长度1
2
3
4
5
6gawk '{
count=length($1)
if (count != 0) {
print count
}
}' out.txtmatch(s, r [,a])
返回
字符串s
中正则表达式r
出现位置的索引。如果指定了数组a
,它会存储s
中匹配正则表达式的那部分。指定的
数组a
中会依次记录数据:匹配正则的起始位置
、正则匹配内容的字符长度
、正则匹配的字符内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22gawk '{
count=match($0, "log", a)
if (count != 0) {
i=1
printf "处理的文本位于: %d | ",FNR
for (var in a) {
if (i%3 == 1) {
printf "匹配字符的起始位置为: %d | ", a[var]
}
if (i%3 == 2) {
printf "正则匹配的内容长度为:%d | ", a[var]
}
if (i%3 == 0) {
printf "正则匹配的内容为:%s\n", a[var]
}
i++
}
}
}' out.txt
你会得到类似如下结果
处理的文本位于: 4 | 匹配字符的起始位置为: 8 | 正则匹配的内容长度为:3 | 正则匹配的内容为:logsplit(s, a [,r])
将
s
用FS
字符或正则表达式r
拆分开,放到数组a
中,并返回数组a
字段的总数1
2
3
4
5
6
7
8
9
10正则r拆分的逻辑我也没弄懂
gawk '{
count=split($0,a, " i")
printf "words is %d\n", count
for (var in a)
{
printf "Loop %d, word is %s\n", FNR, $var
}
printf "----------------------------\n"
}' out.txtsprintf(format, variables)
用提供的
format
和variable
返回一个类似于printf输出的字符串1
2
3
4
5
6
7
8
9
10gawk '{
count=split($0,a)
printf "words is %d\n", count
for (var in a)
{
data=sprintf("Loop %d, word is %s", FNR, $var)
print data
}
printf "----------------------------\n"
}' out.txtsub(r, s [,t])
在
变量$0
或目标字符串t
中查找正则表达式r
的匹配。如果找到了,就用字符串s
替换掉第一处匹配。1
2
3
4gawk '{
count=sub("name", "wuxiang", $1)
printf "%s\n",$0
}' out.txtsubstr(s, i, [,n])
返回
字符串s
中索引值i
开始的n个字符
组成的子字符串。如果未提供n
,则返回s
剩下的部分1
2
3
4gawk '{
string=substr($1,2,3)
printf "%s\n", string
}' out.txttolower(s)
将
字符串s
中的所有字符转换为小写,并将转换后的字符串返回1
2
3
4gawk '{
string=tolower($1)
printf "%s\n", string
}' out.txttoupper(s)
将
字符串s
中的所有字符转换为大写,并将转换结果返回1
2
3
4gawk '{
$1=toupper($1)
printf "%s\n", $0
}' out.txt
对于时间处理,gawk
提供如下函数
mktime(datespec)
将一个按
YYYY MM DD HH MM SS [DST]
格式指定的日期转换为时间戳值1
2
3
4
5gawk '{
中间一定要用" "间隔,不然无法展示内容
date=mktime(2014" "12" "21" "12" "59" "59)
print date
}' out.txtstrftime(format [,timestamp])
将当前时间的
时间戳
或timestamp
转化格式化日期(采用shell函数的date()
)1
2
3
4
5gawk '{
date=systime()
time=strftime("%A, %B %d, %Y", date)
print time
}' out.txt关于
strftime
中format
格式可以参考连接:https://blog.csdn.net/huangzx3/article/details/82792734systime()
返回当前时间的
时间戳
1
2
3
4gawk '{
date=systime()
print date
}' out.txt
gawk
同样支持自定义函数,定义格式如下:
1 | 没有参数的话,就可以不写参数 |
gawk
的函数允许通过return
关键字返回值
1 | function name(i) |
同样也可以在单独的文件中进行定义:
1 | 建议自己将文件与其它文件独立开,加.gawk后缀 |
如果是单独的gawk
脚本函数文件,想要在命令中使用,则可以通过-f
命令将脚本文件引入即可
1 | gawk -f script.gawk -f script file |
至此,gawk
的知识点讲完,补充一句:sed
和gawk
是大爷!