linux-文本处理-awk
awk
-
Linux命令行与shell脚本编程大全(第3版)
格式
#program表示脚本,短的脚本可以直接在命令行中使用
awk 'program' input-file1 input-file2 ...
#长的脚本则可以使用单独的脚本文件
awk -f program-file input-file1 input-file2
简单示例
awk 'BEGIN { print "hello world" }'
脚本必须使用{}大括号,有些示例可以不加BEGIN,但在wsl中不用BEGIN会不执行
独立可执行的awk文件
#! /bin/awk -f
BEGIN { print "Don't Panic!" }
此单独的awk文件可以单独执行,不需要使用上面
awk -f program-file
的格式。注意:
awk文件需要执行
chmod +x filename
,授予可执行的权限
$指定行内数据列
自动给一行中的每个数据元素分配一个变量 ,默认情况下:
- $0代表整个文本行;
- $1代表文本行中的第1个数据字段;
- $2代表文本行中的第2个数据字段;
- $n代表文本行中的第n个数据字段。
文本行中,每个数据字段都是通过字段分隔符划分的,默认是空白字符(tab或空格),也可以通过-F指定分隔符
echo "hello world"|awk '{ print $1}'
hello
echo "hello world"|awk '{ print $2}'
world
#指定0为分隔符
echo "a0b0c0"|awk -F0 '{ print $3}'
c
=号赋值
#将字符串d赋值给第三个字段
echo "a0b0c0"|awk -F0 '{ $3="d";print $3}'
d
BEGIN和END
cat begin1.txt
line 1
line 2
line 3
awk 'BEGIN {print "add new line in the beginning"}; { print $2 };END { print "end fo file"} ' begin1.txt
add new line in the beginning
1
2
3
end fo file
示例2
cat beginscrpit.awk
BEGIN {
print "the latest of users and shells"
print "User ID \t Shell"
print "--------\t------"
FS=":"
};
{
print $1 " \t " $7
}
END {
print "the end of file"
}
awk -f beginscrpit.awk /etc/passwd
the latest of users and shells
User ID Shell
-------- ------
root /bin/bash
daemon /usr/sbin/nologin
bin /usr/sbin/nologin
sys /usr/sbin/nologin
the end of file
BEGIN在读取/etc/passwd文本行前打印开头行,并指定分隔符为:
中间打印第1字段和第7个字段
END读取完文本行后,执行
内置变量
变量 | 描述 |
---|---|
FIELDWIDTHS | 由空格分隔的一列数字,定义了每个数据字段确切宽度 |
FS | 输入字段分隔符,默认是空格或tab |
RS | 输入记录分隔符,默认是换行符 |
OFS | 输出字段分隔符 |
ORS | 输出记录分隔符 |
echo '11:12:13-21:22:23-31:32:33-41:42:43'|awk 'BEGIN { FS=":";RS="-";OFS="~";ORS="&"};{print $1, $2}'
11~12&21~22&31~32&41~42&
默认情况下, gawk将RS和ORS设为换行符。默认的RS值表明,输入数据流中的每行新文本就是一条新纪录。
print命令会自动将OFS变量的值放置在输出中的每个字段间。
echo '123456789 '|awk 'BEGIN {FIELDWIDTHS="2 3 5" }{print $1,$2,$3}'
12 345 6789
一旦设置了FIELDWIDTH变量, gawk就会忽略FS变量,并根据提供的字段宽度来计算字段。
数据变量
变 量 | 描 述 |
---|---|
ARGC | 当前命令行参数个数 |
ARGIND | 当前文件在ARGV中的位置 |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字的转换格式(参见printf语句),默认值为%.6 g |
ENVIRON | 当前shell环境变量及其值组成的关联数组 |
ERRNO | 当读取或关闭输入文件发生错误时的系统错误号 |
FILENAME | 用作gawk输入数据的数据文件的文件名 |
FNR | 当前数据文件中的数据行数 |
IGNORECASE | 设成非零值时,忽略gawk命令中出现的字符串的字符大小写 |
NF | 数据文件中的字段总数 |
NR | 已处理的输入记录数 |
OFMT | 数字的输出格式,默认值为%.6 g |
RLENGTH | 由match函数所匹配的子字符串的长度 |
RSTART | 由match函数所匹配的子字符串的起始位置 |
gawk 'BEGIN{print ARGC,ARGV[1]}' data1
2 data1
gawk 'BEGIN{print ARGC,ARGV[0]}' data1
2 gawk
ARGC变量表明命令行上有两个参数。这包括gawk命令和data1参数(记住,程序脚本并不算参数)。
gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd
fly:/bin/bash
sftpuser:/bin/sh
gitlab-www:/bin/false
git:/bin/sh
NF变量含有数据文件中最后一个数据字段的数字值。可以在它前面加个美元符将其用作字段变量。 NF变量可以让你在不知道具体位置的情况下指定记录中的最后一个数据字段。
gawk '
> BEGIN {FS=","}
> {print $1,"FNR="FNR,"NR="NR}
> END{print "There were",NR,"records processed"}' data1 data1
data11 FNR=1 NR=1
data21 FNR=2 NR=2
data31 FNR=3 NR=3
data11 FNR=1 NR=4
data21 FNR=2 NR=5
data31 FNR=3 NR=6
There were 6 records processed
FNR变量的值在gawk处理第二个数据文件时被重置了,而NR变量则在处理第二个数据文件时继续计数。
自定义变量
awk 'BEGIN { testing="this is a test" ;print testing}'
this is a test
gawk自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头。重要的是,变量名区分大小写。
awk 'BEGIN { x=2;y=x*2+3 ;print y}'
7
gawk编程语言包含了用来处理数字值的标准算数操作符。其中包括求余符号( %)和幂运算符号( ^或**)。
给脚本中的变量赋值
cat begin1.txt
line 1
line 2
line 3
cat script
{
print $n
}
awk -f script n=1 begin1.txt
line
line
line
使用命令行参数来定义变量值会有一个问题。在你设置了变量后,这个值在代码的BEGIN部分不可用。
可以用-v命令行参数来解决这个问题。它允许你在BEGIN代码之前设定变量。
cat script2 BEGIN{print "The starting value is",n; FS=","} {print $n} gawk -v n=3 -f script2 data1 The starting value is 3 data13 data23 data33
正则匹配模式
与sed的匹配模式大同小异,具体参考sed
awk 'BEGIN{FS=":"} /git/{print $1}' /etc/passwd
gitlab-www
git
在/etc/passwd文件中,匹配到git的行输出第一个字段
匹配操作符~
匹配操作符允许将正则表达式限定在记录中特定的数据字段
cat data1
data11,data12,data13,data14,data15
data21,data22,data23,data24,data25
data31,data32,data33,data34,data35
gawk 'BEGIN{FS=","} $1~/data2/{print $0}' data1
data21,data22,data23,data24,data25
gawk 'BEGIN{FS=","} $1!~/data2/{print $0}' data1
data11,data12,data13,data14,data15
data31,data32,data33,data34,data35
表示第一个字段匹配data2则打印整行文本
!~
表达没有匹配,则执行脚本
数学表达式
gawk -F: '$4==0{print $1}' /etc/passwd
root
##也支持以下数学表达式
x == y:值x等于y。
x <= y:值x小于等于y。
x < y:值x小于y。
x >= y:值x大于等于y。
x > y:值x大于y。
参考 /etc/passwd中第四个字段为0的行,即显示所有属于root用户组(组ID为0)的系统用户
结构化命令
if、else
cat numdata
1
2
5
10
20
30
awk '{ if($1>5){ print $1 *2 }else {print $1} }' numdata
1
2
5
20
40
60
while
cat numdata2
1 2 3
4 5 6
10 20 30
gawk '{ total = 0;i = 1;while (i < 4){total += $i;i++;}print total;}' numdata2
6
15
60
for
gawk '{ total = 0;for (i=1;i < 4;i++){total += $i;} print total}' numdata2
6
15
60
时间函数
函数 | 描述 |
---|---|
mktime(datespec) | 将一个按YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳值 |
strftime(format,[timestamp]) | 格式化时间戳,参考linux date命令格式 |
systime() | 返回当前时间搓 |
gawk 'BEGIN { print systime()}'
1625014946
gawk 'BEGIN { print strftime("%Y-%m-%d %H:%M:%S",systime())}'
2021-06-30 09:13:51
gawk 'BEGIN { print mktime("2020 06 21 11 21 24")}'
1592709684
字符串函数
函数 | 描述 |
---|---|
asort(s [,d]) | 按数组s的元素值排序,索引值会被替换为连续数字。如果指定d,则排序后存储在数组d中 |
asorti(s [,d]) | 将数组s按索引值排序。生成的数组会将索引值作为数据元素值。 |
gensub(r, s, h [, t]) | 查找$0或指定字符串t,匹配正则表达式r,如果h为‘g’或‘G’,则全局替换为字符串s,否则如果h为数字则替换为匹配到的第h个文本 |
gsub(r, s [,t]) | 查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果找到了,就全部替换成字符串 |
index(s, t) | 返回字符串t在字符串s中的索引值,如果没找到的话返回0 |
length([s]) | 返回字符串s的长度;如果没有指定的话,返回$0的长度 |
match(s, r [,a]) | 返回字符串s中正则表达式r出现位置的索引。如果指定了数组a,它会存储s中匹配正则表达式的那部分 |
split(s, a [,r]) | 将s用FS字符或正则表达式r(如果指定了的话)分开放到数组a中。返回字段的总数 |
sprintf(format,variables) | 用提供的format和variables返回一个类似于printf输出的字符串 |
sub(r, s [,t]) | 在变量$0或目标字符串t中查找正则表达式r的匹配。如果找到了,就用字符串s替换掉第一处匹配 |
substr(s, i [,n]) | 返回s中从索引值i开始的n个字符组成的子字符串。如果未提供n,则返回s剩下的部分 |
tolower(s) | 将s中的所有字符转换成小写 |
toupper(s) | s中的所有字符转换成大写 |
#排序
cat stringScript
BEGIN {
var["a"]=1
var["g"]=5
var["b"]=3
var["c"]=12
asort(var,test)
for(i in test){
print "index:",i,"-value :",test[i]
}
}
##
gawk -f stringScript
index: 1 -value : 1
index: 2 -value : 3
index: 3 -value : 5
index: 4 -value : 12
#将asort修改为asorti
gawk -f stringScript2
index: 1 -value : a
index: 2 -value : b
index: 3 -value : c
index: 4 -value : g
#替换匹配,替换指定位置
echo a b c a b c | gawk '{ print gensub(/a/, "AA", 2) }'
a b c AA b c
#替换匹配全局替换
#\\2 \\1代表正则表达式中匹配到的第几个()
cat stringScript3
BEGIN {
s = "abc def"
print gensub(/(.+) (.+)/,"\\2 \\1","g",s)
}
#匹配返回索引
gawk 'BEGIN{ print index("123456","23")}'
2
gawk 'BEGIN{ print index("123456","7")}'
0
gawk 'BEGIN{ print index("123456","1")}'
1
#长度
gawk 'BEGIN{ print length("1234561")}'
7
#正则匹配返回索引
gawk 'BEGIN{ print match("122123123",/23/)}'
5
#切分
gawk 'BEGIN{
> s="abcdefg"
> FS="c"
> split(s,a)
> for(i in a){
> print a[i]
> }
> }'
ab
defg
#截取字符串
gawk 'BEGIN{ print substr("122123123",5)}'
23123
自定义函数
gawk '
function> function printStrTwice(s){
> print "s*s="
> return s*s
> }
> BEGIN {
> print printStrTwice(11)
> }
> '
s*s=
121
function关键字 定义函数
return语句返回值
gawk函数库
cat funclib
function myprint()
{
printf "%-16s - %s\n", $1, $4
}
function myrand(limit)
{
return int(limit * rand())
}
function printthird()
{
print $3
}
cat script4
BEGIN{ FS="\n"; RS=""}
{
myprint()
}
#调用
gawk -f funclib -f script4 data2