awk
通过FS作为每一段文本的分割符(默认空格),在命令行上可以用-F参数指定分隔符;
通过RS参数指定文本换行符(默认回车,所以是一行行取数据的),通过换行符作为分割来读取文件。这里区别于sed
,sed
是一行一行读取文件的
? $0 当前所有字段
? $1--$n 按照分隔符分割取到的第n列内容
? FS 分隔符(默认空格) awk ‘BEGIN{FS=":"}{print $1}‘ /etc/passwd ;等价于awk -F: ‘{print $1}‘ /etc/passwd
? RS 换行符(默认回车) awk ‘BEGIN{RS=":"}{print $1}‘ /etc/passwd
? NF 字符列数,当前处理行的分割后的列数 awk -F: ‘{print NF}‘ /etc/passwd
? NR 行号 awk -F: ‘{print NR ":" $1}‘ /etc/passwd
? OFS (默认空格)输出字段分隔符
? ORS (默认回车)输出记录分隔符
? -v:自定义变量
? awk -v host=$HOSTNAME "BEGIN{print host}"
<、>、<=、>=、==、!=、~、!~
~
:用来判断前面的列是否匹配后面的内容。例如awk -F: ‘$7 ~ /^\/bin/{print $0}‘ /etc/passwd
(判断第7列是否以/bin开头,如果是打印该列)
!~
:不匹配
print与printf
print:直接输出
$ awk -F: '{print $1 ":" $2}' /etc/passwd
printf:格式化输出
$ awk -F: '{printf "hello %s, your path is %s\n",$1,$NF}' /etc/passwd
hello root, your path is /bin/bash
hello bin, your path is /sbin/nologin
hello daemon, your path is /sbin/nologin
hello adm, your path is /sbin/nologin
hello lp, your path is /sbin/nologin
....
注意:printf
需要手动增加\n来换行。使用%s
来格式化,printf
外加入要替换的变量
if语句 if(expression){action1}else{action2}
$ seq 10 |awk '{if($0%2==0){print $0"是双数"}else{print $0"是单数"}}'
1是单数
2是双数
3是单数
4是双数
5是单数
6是双数
7是单数
8是双数
9是单数
10是双数
$ awk -F: '$3<10{printf"用户名 -> %s %s uid --> %s\n", $1,"-->",$3}' /etc/passwd
用户名 -> root --> uid --> 0
用户名 -> bin --> uid --> 1
用户名 -> daemon --> uid --> 2
用户名 -> adm --> uid --> 3
用户名 -> lp --> uid --> 4
用户名 -> sync --> uid --> 5
用户名 -> shutdown --> uid --> 6
用户名 -> halt --> uid --> 7
用户名 -> mail --> uid --> 8
$ awk -F: '{if($3<10)printf"用户名 -> %s %s uid --> %s\n", $1,"-->",$3}' /etc/passwd
用户名 -> root --> uid --> 0
用户名 -> bin --> uid --> 1
用户名 -> daemon --> uid --> 2
用户名 -> adm --> uid --> 3
用户名 -> lp --> uid --> 4
用户名 -> sync --> uid --> 5
用户名 -> shutdown --> uid --> 6
用户名 -> halt --> uid --> 7
用户名 -> mail --> uid --> 8
while(expression){action}
例子:使用:分割/etc/passwd
,并将每一列前加上列号
$ awk -F: '{i=1;while(i<=NF){print i":"$i;i++}}' /etc/passwd
1:root
2:x
3:0
4:0
5:root
6:/root
7:/bin/bash
....
设置变量i
初始值为1, while
循环每次循环自增1, 设定条件i
的值永远小于字段数, awk
首先会读一行, 然后会以:
为分隔符读取每一列数据, NF表示字段的总数, 最后打印i
和$i
(i
表示循环每一列的列好, $i
表示每一列的值)
?
for(i=0;i<=10;i++){action}
分割/etc/passwd
,并将每一列前加上列号
$ awk -F: '{for(i=1;i<=NF;i++){print i":"$i}}' /etc/passwd
1:root
2:x
3:0
4:0
5:root
6:/root
7:/bin/bash
当value在array的key中,进行下面的操作。awk
的数组类似python中的字典。
统计/etc/passwd
第7列的值及对应的个数
$ awk -F: '{a[$7]++}END{for(i in a)if(i!=""){print i":"a[i]}}' /etc/passwd
/bin/sync:1
/bin/bash:2
/sbin/nologin:16
/sbin/halt:1
/sbin/shutdown:1
创建数组a,以第7列作为下标,使用运算符++作为数组元素,元素初始值为0。处理/etc/passwd
第一行时,下标是某个路径(/bin/bash
),元素加1,处理第二行时,下标又是一个路径(/sbin/nologin
),元素加1,如果这个路径已经存在在数组a中,则a[/sbin/nologin
]的数组元素加1,也就是这个路径出现了两次,元素结果是2,以此类推。因此可以实现去重,统计出现次数。
核心语句放在END因为需要将数据收集完后才取统计这些数据
array[1]="hello"
array["name"]="Jack"
数组类似python的字典,array[key值]="value值";key为索引,可以是数字也可以是字符串。
数组元素的删除:delete array["key"]
例子:定义了数组a的三个值,并打印结果查看
$ awk 'BEGIN{a[1]="hello";a[2]="word";a["name"]="meitian";for(i in a){print "key为"i":value为"a[i]}}'
key为name:value为meitian
key为1:value为hello
key为2:value为word
把一个字符串分隔为单词并存储在数组中
格式:
split (string, array, field separator)
split (string, array) -->如果第三个参数没有提供,awk
就默认使用当前FS
值。
$ time=`date |awk '{print $5}'`
$ echo $time
20:40:15
$ echo $time|awk '{split($0,a,":");print a[1],a[2],a[3]}'
20 40 15
截取字符串
返回从起始位置起,指定长度之子字符串;若未指定长度,则返回从起始位置到字符串末尾的子字符串。
格式:
substr(s,p)返回字符串s中从p开始的后缀部分
substr(s,p,n)返回字符串s中从p开始长度为n的后缀部分
$ echo "hhp love a man" |awk '{print substr($2,1,4)}'
love
$ echo "hhp love a man" |awk '{print substr($0,1,2)}'
hh
$ echo "hhp love a man" |awk '{print substr($0,1,3)}'
hhp
字符串长度
length函数返回没有参数的字符串的长度。length函数返回整个记录中的字符数
$ echo "hhp love a man" |awk '{print length}'
14
gsub函数则使得在所有正则表达式被匹配的时候都发生替换。
gsub(regular expression,subsitution string,target string);简称gsub (r,s,t)
把一个文件里面所有包含abc的行里面的abc替换成def,然后输出第一列和第三列
$ echo "hhp love a man" |awk '$0 ~/hhp/{gsub("hhp","hhpsb");print $0}'
hhpsb love a man
在awk中,如果调用next,那么next之后的命令就都在执行了,此行文本的处理到此结束,那么读取下一条记录并操作
$ cat test.txt
Tom 2012-12-11 car 53000
John 2013-01-13 bike 41000
vivi 2013-01-18 car 42800
Tom 2013-01-20 car 32500
John 2013-01-28 bike 63500
$ awk '{if(NR==1){next}print $0}' test.txt
John 2013-01-13 bike 41000
vivi 2013-01-18 car 42800
Tom 2013-01-20 car 32500
John 2013-01-28 bike 63500
$ cat netstat.txt
Proto Recv-Q Send-Q Local-Address Foreign-Address State
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
tcp 0 0 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2
tcp 0 0 coolshell.cn:80 11.194.134.189:1032 ESTABLISHED
tcp 0 0 coolshell.cn:80 123.169.124.11:49809 ESTABLISHED
tcp 0 0 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2
tcp 0 0 coolshell.cn:80 12.16.124.111:49829 ESTABLISHED
tcp 0 0 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT
tcp 0 4166 coolshell.cn:80 61.148.24.38:30901 ESTABLISHED
tcp 0 1 coolshell.cn:80 12.152.181.209:26825 FIN_WAIT1
tcp 0 0 coolshell.cn:80 110.194.134.189:47 ESTABLISHED
$ awk {'print $1,$3'} netstat.txt
其中单引号中的被大括号括着的就是
awk
的语句,注意,其只能被单引号包含
其中的$1..$n
表示第几例。注:$0
表示整个行
如果换成双引号, awk
语法就不起作用了
输入$1-$9
,都会显示文件中的所有内容
$ awk {"print $1"} netstat.txt
Proto Recv-Q Send-Q Local-Address Foreign-Address State
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
tcp 0 0 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
tcp 0 0 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2
tcp 0 0 coolshell.cn:80 11.194.134.189:1032 ESTABLISHED
tcp 0 0 coolshell.cn:80 123.169.124.11:49809 ESTABLISHED
tcp 0 0 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2
tcp 0 0 coolshell.cn:80 12.16.124.111:49829 ESTABLISHED
tcp 0 0 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT
tcp 0 4166 coolshell.cn:80 61.148.24.38:30901 ESTABLISHED
tcp 0 1 coolshell.cn:80 12.152.181.209:26825 FIN_WAIT1
tcp 0 0 coolshell.cn:80 110.194.134.189:47 ESTABLISHED
输入$0
就会输出0
, 文件中有多少行就输出多少行0
awk {"print $0"} netstat.txt
0
0
0
0
0
0
0
0
0
0
0
0
0
0
输入$10
以后的数字就会输出的值和echo $10
是一样的, 文件中有多少行就输出多少行0
$ awk {"print $10"} netstat.txt
0
0
0
0
0
0
0
0
0
0
0
0
0
0
$ echo $10
0
echo $10
规律
会首先输出echo $1, 然后在输出后面的数字
echo $01
规律:
会首先输出echo $0, 然后在输出后面的数字
$ echo $10
0
$ echo $11
1
$ echo $19
9
$ echo $190
90
$ echo $01
-bash1
$ echo $1
$ echo $01ssdsd
-bash1ssdsd
$ awk '{print $1,"-->",$NF}' netstat.txt
这里$NF
来表示最后一列
0
&& 第6列的值为LISTEN
$ awk '{print $3==0,$6}' netstat.txt
其中的==
为比较运算符。其他比较运算符:!=
, <
, <
>=
0
, 并且打印对应的第6列的信息$ awk '{print $3==0,$6}' netstat.txt
$ awk '$3>0{print $1,$5}' netstat.txt
$ awk -F: '$3<10{printf"用户名 -> %s %s uid --> %s\n", $1,"-->",$3}' /etc/passwd
用户名 -> root --> uid --> 0
用户名 -> bin --> uid --> 1
用户名 -> daemon --> uid --> 2
用户名 -> adm --> uid --> 3
用户名 -> lp --> uid --> 4
用户名 -> sync --> uid --> 5
用户名 -> shutdown --> uid --> 6
用户名 -> halt --> uid --> 7
用户名 -> mail --> uid --> 8
$ awk -F: '{if($3<10)printf"用户名 -> %s %s uid --> %s\n", $1,"-->",$3}' /etc/passwd
用户名 -> root --> uid --> 0
用户名 -> bin --> uid --> 1
用户名 -> daemon --> uid --> 2
用户名 -> adm --> uid --> 3
用户名 -> lp --> uid --> 4
用户名 -> sync --> uid --> 5
用户名 -> shutdown --> uid --> 6
用户名 -> halt --> uid --> 7
用户名 -> mail --> uid --> 8
$ awk -F: '{print $1,$6,$7}' /etc/passwd # 输出用户名,家目录,解释器
-F
的意思就是指定分隔符
注:如果你要指定多个分隔符,你可以这样来:
awk -F '[;:]'
以\t
作为分隔符输出
$ awk -F: '{print $1, $6, $7}' OFS='\t' /etc/passwd # 输出用户名,家目录,解释器, 三列数据之间使用\t相隔
OFS
表示的是分隔符
awk '{print NR, "->", $1, $6, $7, "字段数","=>",NF}' OFS='\t' netstat.txt
NR(Number of Record)
表示的是已经处理过的总记录数目,或者说行号(不一定是一个文件,可能是多个)
NF(Number for Field)
表示的是,一条记录的字段的数目.
~
表示模式开始 |/ /
是模糊搜索的模式 |!~
表示模式取反
$ awk '$6~/FIN/{print NR,$4,$5,$6}' netstat.txt
$ awk '$6~/FIN|TIME/ {print NR,$4,$5,$6}' netstat.txt
5 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT
6 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2
9 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2
11 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT
13 coolshell.cn:80 12.152.181.209:26825 FIN_WAIT1
$ awk '$6~/FIN/ {print NR,$4,$5,$6}' netstat.txt
/sbin/nologin
的数据$ awk '$NF~/\/bin\/bash/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
java:x:1000:1000:java:/home/java:/bin/bash
grep
一样的去匹配$ grep 'LISTEN' netstat.txt
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
$ awk '/LISTEN/' netstat.txt
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
$ awk '/LIST/ {print NR,$0}' netstat.txt
2 tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
3 tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
4 tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
$ grep -n 'LIST' netstat.txt
2:tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN
3:tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
4:tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN
将
awk
输出的内容重定向到一个文件
$ awk 'NR!=1{print $6}' netstat.txt > col6.txt
$ cat col6.txt
/etc/passwd
第7列的值及对应的个数$ awk -F: '{a[$7]++}END{for(i in a)if(i!=""){print i":"a[i]}}' /etc/passwd
/bin/sync:1
/bin/bash:2
/sbin/nologin:16
/sbin/halt:1
/sbin/shutdown:1
创建数组a,以第7列作为下标,使用运算符++作为数组元素,元素初始值为0。处理/etc/passwd
第一行时,下标是某个路径(/bin/bash
),元素加1,处理第二行时,下标又是一个路径(/sbin/nologin
),元素加1,如果这个路径已经存在在数组a中,则a[/sbin/nologin
]的数组元素加1,也就是这个路径出现了两次,元素结果是2,以此类推。因此可以实现去重,统计出现次数。
核心语句放在END因为需要将数据收集完后才取统计这些数据
$ awk 'NR!=1{a[$6]++;} END {for (i in a) print i ", " a[i];}' netstat.txt
LISTEN, 3
ESTABLISHED, 5
FIN_WAIT1, 1
FIN_WAIT2, 2
TIME_WAIT, 2
$ ps aux | awk 'NR!=1{a[$1]+=$6;} END { for(i in a) print i ", " a[i]"KB";}'
chrony, 2084KB
dbus, 2596KB
polkitd, 13968KB
postfix, 8156KB
root, 127468KB
创建数组a,以第1列作为下标,使用运算符+=$6作为数组元素,元素初始值为0。处理ps aux
第一行时,下标是某个用户, 值是这个用户所在行的第六列, 元素的值加上第六列的值, 这样以此类推, 可以将每个用户所起的进程的总内存进行统计
核心语句放在END因为需要将数据收集完后才取统计这些数据
ps aux每一列参数解释
[root@git ~]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.6 127968 6552 ? Ss 09:23 0:01 /usr/lib/systemd/systemd --switched-root --system --deseri
root 2 0.0 0.0 0 0 ? S 09:23 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S 09:23 0:00 [ksoftirqd/0]
root 5 0.0 0.0 0 0 ? S< 09:23 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 09:23 0:00 [kworker/u256:0]
root 7 0.0 0.0 0 0 ? S 09:23 0:00 [migration/0]
root 8 0.0 0.0 0 0 ? S 09:23 0:00 [rcu_bh]
root 9 0.0 0.0 0 0 ? R 09:23 0:00 [rcu_sched]
root 10 0.0 0.0 0 0 ? S< 09:23 0:00 [lru-add-drain]
...
USER: 运行进程的用户
PID 进程的ID号
%CPU 进程占用CPU的百分比
%MEM 进程占用内存的百分比
VSZ 进程占用虚拟内存的百分比
RSS 占用固定内存的百分比
TTY 进程在哪个终端运行, 如果和终端无关, 会显示?; 如果显示pts/0, 则表示通过网络连接主机并运行进程
STAT 进程的运行状态, linux下一共有5种状态
START 进程的开始时间
TIME 进程的执行时间
COMMAND 进程的执行路径
Tom 2012-12-11 car 53000
John 2013-01-13 bike 41000
vivi 2013-01-18 car 42800
Tom 2013-01-20 car 32500
John 2013-01-28 bike 63500
输出一月份每个员工的工资:
$ awk '{split($2,a,"-");if(a[2]=01){sum[$1]+=$4}}END{for(i in sum) print sum[i],i}' test.txt
42800 vivi
85500 Tom
104500 John
将/sbin/nologin
替换成/bin/bash
$ cp /etc/passwd passwd
$ awk -F: '$NF~/\/sbin\/nologin/{print $1,"-->",$NF}' passwd
bin --> /sbin/nologin
daemon --> /sbin/nologin
adm --> /sbin/nologin
lp --> /sbin/nologin
mail --> /sbin/nologin
operator --> /sbin/nologin
games --> /sbin/nologin
ftp --> /sbin/nologin
nobody --> /sbin/nologin
systemd-network --> /sbin/nologin
dbus --> /sbin/nologin
polkitd --> /sbin/nologin
tss --> /sbin/nologin
sshd --> /sbin/nologin
postfix --> /sbin/nologin
chrony --> /sbin/nologin
$ awk -F: '{gsub("/sbin/nologin","/bin/bash")}$NF~/\/bin\/bash/{print $1,"-->",$NF}' passwd
root --> /bin/bash
bin --> /bin/bash
daemon --> /bin/bash
adm --> /bin/bash
lp --> /bin/bash
mail --> /bin/bash
operator --> /bin/bash
games --> /bin/bash
ftp --> /bin/bash
nobody --> /bin/bash
systemd-network --> /bin/bash
dbus --> /bin/bash
polkitd --> /bin/bash
tss --> /bin/bash
sshd --> /bin/bash
postfix --> /bin/bash
chrony --> /bin/bash
java --> /bin/bash
核心代码段是 {gsub("/sbin/nologin","/bin/bash")}
, 将passwd文件中每一行中的 /sbin/nologin
替换成 /bin/bash
$NF~/\/bin\/bash/{print $1,"-->",$NF}
这段代码就是将处理好的文件进行过滤, 将文件中/bin/bash
的字符串匹配出来, 并输出第一列和最后一列
$ echo "hhp love a man" |awk '{print substr($2,1,4)}'
love
$ echo "hhp love a man" |awk '{print substr($0,1,2)}'
hh
$ echo "hhp love a man" |awk '{print substr($0,1,3)}'
hhp
$ echo "hhp love a man" |awk '{print length}'
14
$ cat test.txt
Tom 2012-12-11 car 53000
John 2013-01-13 bike 41000
vivi 2013-01-18 car 42800
Tom 2013-01-20 car 32500
John 2013-01-28 bike 63500
$ awk '{if(NR==1){next}print $0}' test.txt
John 2013-01-13 bike 41000
vivi 2013-01-18 car 42800
Tom 2013-01-20 car 32500
John 2013-01-28 bike 63500
原文:https://www.cnblogs.com/cjwnb/p/11608804.html