linux三剑客 awk
awk
本文是基于菜鸟教程awk的学习笔记和总结
awk是一个文本处理工具,它会对文件的每一行进行处理,输出结果。
基础语法:
awk '{pattern actions}' files # awk可以处理多个文件,注意是单引号
常用变量:
FS:文本分隔符,针对文本的每行,按照此分隔符分割
NF:当前行分割后的字段数
NR:当前行数
RS:当前行的分隔符,通常是\n,awk用此变量来分割确定每行数据。
$0:当前行的内容
$1:当前行的第一个字段;同理$2,$3,$4......
IGNORECASE:值为1或0(真或假),用来进行是否忽略大小写匹配。
FILENAME:当前文件名
OFS:输出分隔符,它是用print输出信息时,这些变量之间的分割符号。
ORS:输出行分隔符,输出时每行的分隔符
FNR:当处理多个文件时,FNR显示的是当前文件的第几行。NR显示的是当前处理了多少行(多个文件时会累加)
常用参数:
-F:后接分隔符号,指定字段分割的符号,可以使用正则表达式。如:awk -F '[/:,]+' '{print $1}' file
-v:后接变量,用来声明一个变量。如:awk -v a=1 '{a++} {print a}' file
-f:后接awk文件,从文件读取awk命令。awk -f file.awk file2
示例:
现有文件a:
$ cat a
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
-F:分割上述文本:
$ awk -F '[:/]' '{print $1,$2}' a # 用 :/ 这两个分隔符来分割
root x
bin x
daemon x
$ awk -F':' 'BEGIN{OFS="=";} {print $1,$2}' a # 改变变量的分割符
root=x
bin=x
daemon=x
$ awk -F '[:/]' '{print $1 $2}' a # print后面的变量用空格隔开,则字段直接合并了
rootx
binx
daemonx
-v:自定义变量:
$ awk -v count=0 '{count++;print count}' a # {}里面可以写多个action,并用;隔开
1
2
3
常用变量的使用:
$ awk -F '[:/]' '{ print NR,NF,$0}' a
1 10 root:x:0:0:root:/root:/bin/bash
2 10 bin:x:1:1:bin:/bin:/sbin/nologin
3 10 daemon:x:2:2:daemon:/sbin:/sbin/nologin
打印某行的信息:
$ awk '{if(NR>1 && NR<4) print NR,$0}' a
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
正则匹配某个字段:
- ~:针对字段使用正则匹配;如针对整行使用正则,可以省略;
- /*/:两个/之间的内容,代表匹配的模式
- !~:不匹配某个模式,针对字段使用,如针对整行,可以省略;
- !/*/:不匹配两个/之间的内容
$ awk -F ':' '$1 ~ /root/' a # 针对字段1,匹配root
root:x:0:0:root:/root:/bin/bash
$ awk '/sbin/' a # 针对整行,匹配sbin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
$ awk -F ':' '$1 !~ /root/' a # 匹配字段1不是root的行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
$ awk '!/sbin/' a # 匹配行中不带sbin的行
root:x:0:0:root:/root:/bin/bash
忽略大小写匹配:
$ awk 'BEGIN{IGNORECASE=1} /ROOT/' a
root:x:0:0:root:/root:/bin/bash
BEGIN,END:
awk 是对文件的每行进行处理,也就是说只有处理每行时,语句才会执行。如果你想要在处理文件的第一行之前,进行一些预设操作,需要用到BEGIN;如果想要在文件处理完最后一行之后,进行一些扫尾的操作,需要用到END。也就是说,BEGIN,END在处理整个文件的过程中,只在开头前和结尾后执行一次,在处理每行的过程中,它们是不会执行的。
# BEGIN预定义一个count=0,每处理一行count+1,处理完最后一行后,END进行打印总数的操作。
$ awk 'BEGIN{count=0} {count++} END{print "The file has line:",count}' a
The file has line: 3
数组:
AWK 可以使用关联数组这种数据结构,索引可以是数字或字符串。
AWK关联数 组也不需要提前声明其大小,因为它在运行时可以自动的增大或减小。
语法:
array[index]=value # 声明数组
delete array[index] # 删除某个索引的值
for (item in array) print array[item] # 数组的遍历,遍历的是索引,取值需要 array[item]
声明数组并赋值:
$ awk 'BEGIN{site["name"]="wang";site[1]=1;} END{print site["name"],site[1]}' a
wang 1
$ awk 'BEGIN{site["name"]="wang";site[1]=1;} END{ delete site["name"]; print site["name"],site[1]}' a # 中间删除了name索引
1
统计文件词频:
$ awk -F '[:/]' '{ for(a=1;a<=NF;a++) {if($a in array) array[$a]++ ;else if(length($a)!=0) array[$a]=1; }} END{for(aa in array) printf "%-10s %s\n", aa,array[aa]} ' a | sort -t ' ' -nr -k2
bin 4
x 3
sbin 3
root 3
nologin 2
daemon 2
2 2
1 2
0 2
bash 1
逻辑判断:
条件判断之:if
语法:
if (condition)
action
if (condition){
action;
action;
}
例如:
$ awk '{if(NR==2) print $0}' a
bin:x:1:1:bin:/bin:/sbin/nologin
$ awk '{if(NR==2) {print NR;print $0}}' a
2
bin:x:1:1:bin:/bin:/sbin/nologin
条件判断之:if...else...
语法:
if(condition)
action1;
else
action2;
示例:
zhog@GG678S2 MINGW64 ~/Desktop
$ awk '{if(NR==2) print NR,$0; else print NR }' a
1
2 bin:x:1:1:bin:/bin:/sbin/nologin
3
条件语句之:if...else if... else if ... else ...
语法:
if(condition)
action1;
else if(condition)
action2;
else if(condition)
action3;
...
else
action4;
示例:
$ awk '{if(NR==2) print NR,$0; else if(NR==3) print NR; else print "This is NR 1" }' a
This is NR 1
2 bin:x:1:1:bin:/bin:/sbin/nologin
3
for 循环
语法:
for (initialisation; condition; increment/decrement)
action
示例:
$ echo "haha" | awk '{ for (i=1;i<=10;i++) print i }'
1
2
3
4
5
6
7
8
9
10
while 循环:
语法:
while (condition)
actions;
示例:
$ echo "haha" | awk '{ a=1; while(a<=10) {print a;a++ } }'
1
2
3
4
5
6
7
8
9
10
break 跳出循环:
$ echo "haha" | awk '{ a=1; while(a<=10) {print a;a++;break } }'
1
continue 继续循环:
continue会让跳过循环内的代码,直接进行下一次循环
$ echo "haha" | awk '{ a=1; while(a<=10) {if (a==5) {a++;continue;} print a;a++;} }'
1
2
3
4
6
7
8
9
10
exit 退出脚本
$ echo "haha" | awk '{ a=1; while(a<=10) {print a;a++; if(a==5) exit; } }'
1
2
3
4
字符函数:
字符替换:gsub,sub
gsub 将替换 str 中所有的 old 为 new,而 sub 只替换一次,两者语法相同:
gsub(old,new,str) / sub(old,new,str)
示例:
$ echo 'haha' | awk '{ str="hahahahaha"; gsub("ha","I",str);print str }'
IIIII
$ echo 'haha' | awk '{ str="hahahahaha"; sub("ha","I",str); print str }'
Ihahahaha
字符截取 substr
语法:
substr(str, start, end) # 截取 str 的start-end(包含start,end),如果没写end,则到最后
示例:
$ echo 'haha' | awk '{ str="hahahahaha"; s = substr(str,1,2); print s}'
ha
字符索引 index
语法:
index(str1,str2) # 返回 str2 在 str1 中的索引位置,如果没有找到,返回0
示例:
$ echo 'haha' | awk '{ str="one t two three"; s = "two"; print index(str,s)}'
7
字符串长度:length
语法:
length(str)
示例:
$ echo 'haha' | awk '{ str="one t two three"; print length(str)}'
15
字符串分割 split
语法:
split(str,array,',') # 将str用','分割,每个元素存放在arrary里面,如果没指定分隔符,则用 FS
示例:
$ echo 'haha' | awk '{ str="one t two three"; split(str,array); for (arr in array) print array[arr]; }'
one
t
two
three
字符串大小写转换:tolower(),toupper()
语法:
tolower(str) / toupper(str)
示例:
$ echo 'haha' | awk '{ str="one two three"; print tolower(str) "\n" toupper(str) }'
one two three
ONE TWO THREE
时间
获取时间戳 systime
语法:
systime() # 返回从1970年1月1日开始到当前时间(不计闰年)的整秒数
示例:
$ echo 'haha' | awk '{ print systime(); }'
1602483391
时间戳格式化输出 strftime
语法:
strftime(format,timestamp)
示例:
$ echo 'haha' | awk '{ print strftime("Time = %m/%d/%Y %H:%M:%S", systime()) }'
Time = 10/12/2020 14:20:41
其他命令
执行系统命令 system(command)
执行特定的命令然后返回其退出状态。返回值为 0 表示命令执行成功;非 0 表示命令执行失败。
示例:
$ echo 'a' | awk '{ ls=system("ls 'chrom*'"); print ls}'
chrome.lnk # 这句话是 ls=system("ls 'chrom*'") 执行时显示的
0 # 这个是 print ls 才显示的。
跳过当前行,处理下一行数据 next
next 可以让 awk 停止处理当前记录,直接进行下一行记录的处理
$ awk '{ if(NR==2) next; print $0; }' a
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
问题:awk什么时候该加{}啊?
首先,awk的语法是awk '{pattern action}' file
, 其中 pattern,其实就是各种判断语句(if,while等各种条件),action 就是正常的执行操作(如赋值:a=1; 打印:print a;),当在一个 pattern 下进行多种操作时(多个action,每个action用;隔开,如a=1;a++;print a;),为了保证这些action都是作用于当前pattern下,需要把这些actions用{}包裹起来。否则,就只有pattern后跟的第一个 action 作用于当前 pattern 下。
譬如:下面是个死循环哦
$ echo 'haha' | awk '{a=1; while(a<10) print a; a++; }'
# 格式化一下上面的语句,其实是这样的:
echo 'haha' | awk '{
a=1;
while(a<10)
print a;
a++; # 因为a++;不在循环里,所以是个死循环
}'
不想变成死循环,加个{}就好了:
$ echo 'haha' | awk '{a=1; while(a<10) {print a; a++;} }'
1
2
3
4
5
6
7
8
9
# 上面的语句等价于:
echo 'haha' | awk '{
a=1;
while(a<10) {
print a;
a++; # a++; 在循环里面,每次+1,当a=10就跳出循环了。
}
}'
附录:运算符
运算符 | 描述 |
---|---|
=, +=, -=, *=, /=, %=, ^=, **= | 赋值 |
?: | 三目运算;echo "a" | awk '{a=1;print a==2?"yes":"no"}' 结果是no |
|| | 逻辑或 |
&& | 逻辑与 |
~ 和 !~ | 匹配正则表达式和不匹配正则表达式 |
<, <=, >, >=, !=, == | 关系运算符 |
空格 | 连接 |
+, - | 加,减 |
*, /, % | 乘,除与求余 |
+, -, ! | 一元加,减和逻辑非 |
^, *** | 求幂 |
++, -- | 增加或减少,作为前缀或后缀;如a=1;a++ |
$ | 字段引用;如 $1 |
in | 数组成员 |