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 数组成员
posted @ 2020-10-12 15:33  wztshine  阅读(219)  评论(0编辑  收藏  举报