Linux-grep、sed、awk之awk命令
参考:
Linux三剑客(grep sed awk) 之 awk
awk从放弃到入门
The GNU Awk User’s Guide
awk
是一个报告生成器,支持条件判断、数组、循环等功能,可以将文本整理成我们想要的表格形式。awk逐行处理,默认按照空格作为分隔符,若有多个空格,则将连续的空格作为分隔符。
一、awk基础
- 普通模式
awk [options] 'program' file1,file2
program = pattern+action
awk [options] 'Pattern{Action}' file
awk常用动作为print和printf
- 特殊模式
awk 'BEGIN{coms}/pattern/{coms}END{coms}'
1)BEGIN和END模块只能有一个,中间的pattern匹配可以有多个
2)BEGIN:代表awk在处理文本前要做的事情,即使无文件名,仅用BEGIN也能正常输出
awk 'BEGIN{print "111","222"}'
3)END:代表awk在处理文本后要做的事
awk '{print 1,2}END{print'333','444'}'test
- 内置变量
变量名 | 属性 | 备注 |
---|---|---|
$0 | 当前一整行 | |
$1~$n | 当前记录的第n个字段 | awk '{print "str:"$1'}' |
FS | 输入字段分隔符,默认为space | awk -F ':' awk -v FS=':' |
RS | 输入记录分隔符,默认为\n | 如果RS=“”,会以空白行分隔 |
OFS | 输出字段分隔符,默认space | |
ORS | 输出的记录分隔符,默认\n | |
NF | 当前记录中的字段个数,即多少列 | |
NR | 行号,从1开始 | |
FNR | 各文件分别显示行号 | |
FILENAME | 当前文件名 | awk '{print FILENAME,$0}' |
ARGC | 命令行参数的个数 | |
ARGV | 数组,保存的是命令行给定的参数 | ARGV[0]是awk,单引号中的不算参数,处理的文件是参数 |
- 自定义变量
1)-v varname=value
$awk -v myVar="test" 'BEGIN{print myVar}'
$abc=666
$awk -v myvar=$abc 'BEGIN{print myvar}'
2)在program中定义
awk 'BEGIN{myvar="ttt";print myvar}'
tips:赋值时不要在等号左右加空格,我写R的时候习惯性啥都加空格,于是就报错啦
二、printf
- echo和printf
两者都是输出文本命令,echo输出字符串末尾会自动加上\n;printf则不会,\n需要指定。
printf的优势在于可以用格式替换符来处理一长串的str
格式替换符
名称 | 含义 |
---|---|
%s | 字符串 |
%f | 浮点格式 |
%b | 对应参数包含转义字符时,对应转义字符会被转义 |
%c | ASCII字符,显示对应擦书第一个字符 |
%d,%i | 十进制整数 |
%o | 不带正负号的八进制值 |
%u | 不带正负号的十进制值 |
%x | 不带正负号的十六进制值,用af表示1015 |
%X | 不带正负号的十六进制,用AF表示1015 |
%% | 表示‘%’本身 |
转义字符
名称 | 含义 |
---|---|
\a | 警告字符,常为ASCII的BEL字符 |
\b | 后退 |
\c | 不显示 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | “\”本身 |
\ddd | 表示1~3位数八进制的字符串,仅在字符格式串中有效 |
\0ddd | 表示1~3位数八进制值的字符串 |
修饰符
参数 | 含义 |
---|---|
%7s | 7代表当前替换符对应的输出长度为7个字符宽,不足补齐,超出也正常显示 |
%-7s | “-”表示左对齐,不加时为右对齐 |
%+5d | 正数会自动变为+num |
%12.3f | “.3”表示保留小数点后三位,%f默认是小数点后6位 |
%12.5d | “.5”表示正数的长度,不足用0补齐 |
- awk的printf动作
注意:
输出文本不会换行,要手动加\n;
“指定的格式”与“被格式化的文本”间要用“逗号”隔开
“格式替换符”与“被格式化的文本”数量一一对应
$ awk -v FS=':' 'BEGIN{printf "%-10s\t %s\n","用户名称","用户ID"} {printf "%-10s\t %s\n",1,3}' /etc/passwd
用户名称 用户ID
1 3
1 3
1 3
三、awk模式(Pattern)
- grep和awk
grep '^root' /etc/password
awk '/^root/{print $0}' /etc/passwd
- 注意
1)awk中的正则是扩展正则表达式
2)在使用{x,y}或[[:space]]时,要加上参数--posix
3)注意""和"."的转义
3.awk行范围模式
awk '/正则表达式/{动作}' file
awk '/正则1/,/正则2/{动作}' file
两个正则就是行范围模式,从正则1匹配的行开始,到正则2匹配的行结束
匹配符 | 含义 |
---|---|
~ | 匹配后面的正则 '$1~/pattern/' |
!~ | 不匹配后面的正则 '$1!~/pattern/' |
示例
$ cat test.txt
sdsfhu
djfia
I love you
I hate you
Oh/my god
$ awk '/^Oh\//{print $0}' test.txt
Oh/my god
$ awk --posix '/[[:space:]]/{print $0}' test.txt
I love you
I hate you
Oh/my god
$ awk '/love/,/hate/{print $0}' test.txt
I love you
I hate you
$ awk 'NR>=2 && NR<=4{print $0}' test.txt
djfia
I love you
I hate you
四、awk动作总结
{}:“组合语句”类型的动作,将多个代码组合成代码块
print $0:“输出语句”类型
- if动作
$ awk '{if(NR == 1){print $0}}' test.txt
sdsfhu
#if对应的{},若只有一条命令,可以省略{}
$ awk '{if(NR == 1) print $0}' test.txt
sdsfhu
$ awk '{if(NR == 1){print $1;print $2}}' test.txt
$ awk -F ":" '{if($3 <500){print $1,"系统用户"}else {print $1,"普通用户"}}' file
- for
$ awk 'BEGIN{for(i=1;i<=6;i++){print i}}'
1
2
3
4
5
6
3.while
$ awk -v i=1 'BEGIN{while(i<=5){print i;i++}}'
1
2
3
4
5
$ awk 'BEGIN{i=1;while(i<=5){print i;i++}}'
1
2
3
4
5
- do...while
#打印1遍test
$ awk 'BEGIN{i=1;do{print "test";i++}while(i<1)}'
test
#打印5遍test
$ awk 'BEGIN{do{print "test";i++}while(i<=5)}'
test
test
test
test
test
test
- continue和break
#打印0~5,但不打印3
$ awk 'BEGIN{for(i=0;i<6;i++){if(i==3){continue};print i}}'
0
1
2
4
5
#打印0~2
$ awk 'BEGIN{for(i=0;i<6;i++){if(i==3){break};print i}}'
0
1
2
- exit
表示跳过所有后续动作,直接执行END内的命令;如果没有END模式,则直接结束awk命令
#只打印1
$ awk 'BEGIN{print 1;exit;print 2;print 3}'
1
#不打印$0
$ awk 'BEGIN{print "start";exit} {print $0} END{print "over"}' test.txt
start
over
- next
跳过当前行,直接从下一行开始处理
#跳过第二行
$ awk '{if(NR==2){next};print $0}' test.txt
sdsfhu
I love you
I hate you
Oh/my god
这些命令感觉和C语言中的差不多~
五、awk数组
- 基础概念
1)awk中数组是一个使用字符串作为下标的“关联数组”
2)使用时可用数字/字符串作为下标,使用数字作为下标时,awk默认把“数字”下标转换为“字符串”
3)awk中,元素值可以设置为“空”
4) 当一个元素不存在于数组时,若直接引用这个不存在的元素,awk会自动创建这个元素,并默认为这个元素赋值为“空字符串”
5) awk中,判断数组元素中是否存在,用“if(下标 in 数组名)”
6)split函数生成的数组,下标默认从1开始 - 基本操作
#创建数组,可直接创建
$ awk 'BEGIN{ a[0]="一";a[1]="二";a[2]="三";print a[0]}'
一
#删除数组元素
$ awk 'BEGIN{a[0]="一";a[1]="二";a[2]="三";delete a[0]}'
#删除整个数组
$ awk 'BEGIN{a[0]="一";a[1]="二";a[2]="三";delete a}'
#for循环有序遍历数组,下标需要是数字
$ awk 'BEGIN{a[0]="一";a[1]="二";a[2]="三";for(i=1;i<=2;i++){print i,a[i]}}'
1 二
2 三
#for循环无序遍历数组
$ awk 'BEGIN{a[0]="一";a[1]="二";a[2]="三";for(i in a){print i,a[i]}}'
0 一
1 二
2 三
六、awk内置函数
- 算术函数
1)rand 生成随机数
2)srand 固定打印1,与rand合用可生成小于1的随机数
3)int 取整
awk 'BEGIN{srand();print rand()}'
#随机数乘以100,通过int函数调整,生成0~100间的随机数
awk 'BEGIN{srand();print int(100*rand())}'
- 字符串函数
1)gsub:将指定范围内匹配到的字符全部替换为新字符
2)sub:与gsub类似,但只能替换范围内第一次匹配到的字符
awk '{gsub("you","me",1);print 0}' test.txt
awk '{sub("you","me");print }' test.txt
3)length:获取指定字符串长度
4)index:获取指定字符串位于整个字符串中的位置
5)split:返回数组长度
- 排序函数
1)asort:根据数组value值进行排序。排序后,原有key会被数字替代
2)asorti:排列原数组
七、三元运算
语法
条件?结果1:结果2
1.三元运算替换if…else
#if...else
awk -F : '{if($3<500){usertype="系统用户"}else{usertype="普通用户"};\print $1,usertype}' /etc/password
#三元运算符
awk -F : '{usertype=$3<500?"系统用户":"普通用户";\print
$1,usertype}' /etc/passwd
$3<500:条件判断
?"系统用户":为真,则usertype=系统用户
:"普通用户":为假,则usertype=普通用户
#统计系统用户和普通用户数量
awk -F : '{$3<500?a++:b++}END{print a,b}' /etc/passwd
- 打印奇数偶数行
#打印奇数行
$ awk 'i=!i' test.txt
sdsfhu
I love you
Oh/my god
#打印偶数行
$ awk '!(i=!i)' test.txt
djfia
I hate you
解析:
1)awk中,若省略了模式对应的动作,当前行满足模式时,默认动作为打印整行,即{print $0}
2)awk中,0或者空字符串表示“假”,非0值或者非空字符串表示“真”
$ awk '/o/{print $0}' test.txt
I love you
I hate you
Oh/my god
$ awk '/o/' test.txt
I love you
I hate you
Oh/my god
从上可看出,若省略了对应动作,默认输出整行,即'{print $0}'。BEGIN/END模式除外
那么将模式替换为“真”或“假”
接下来的可以自仔细琢磨参考博文的内容:https://www.zsythink.net/archives/2159
我自己讲不清楚了/捂脸,慢慢悟吧呜呜
$ awk '{i=!i;print i}' test.txt
1
0
1
0
1
可以看到,打印出i值,第一行为1,;然后再取反值,则为0
因此,在打印奇数行时,因为i最开始不为0,所以这一行被打印
(我也被自己绕晕了,总之就是这样能打印出来奇数行。。)
偶数行同理,只不过是再取一个反值