[root@hass-11 ~]# vmstat
进程 内存 交换分区 磁盘 系统 CPU
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 1140540 2076 625804 0 0 1 0 28 28 0 0 100 0 0
小用法,使用awk来对文件随机抽取n行
awk 'BEGIN{srand()} {print rand()"\t"$0}' input_file | sort -nk 1 | head -n line_num | awk -F "\t" '{print $2}'
简介
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为样式扫描和处理语言。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
命令格式
awk 选项 '操作' 文件
或
其他命令 | awk 选项 '操作'
#花括号 {}不需要在程序中始终出现,但它们用于根据特定的模式'对一系列指令进行分组'。
#要表示的正则表达式,用'斜杠'括起来
#通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。
#awk 相当于 grep + cut
#awk 逐行读取,以指定分隔符分割字符串,赋值给变量
选项
-F
# 指定输入分隔符,awk会以该分隔符为准来切定位到的行,默认以空格或连续的空格为分隔符
操作
操作=定位+命令
定位
行号
正则
命令
在读入所有行之前进行的操作 读一行执行一次的操作 所有行都读完后执行的操作
BEGIN{} 定位{} END{}
条件类型 | 条 件 | 说 明 |
---|---|---|
awk保留字 | BEGIN | 在 awk 程序一开始,尚未读取任何数据之前执行。BEGIN 后的动作只在程序开始时执行一次 |
awk保留字 | END | 在 awk 程序处理完所有数据,即将结束时执行?END 后的动作只在程序结束时执行一次 |
关系运算符 | > | 大于 |
< | 小于 | |
>= | 大于等于 | |
<= | 小于等于 | |
== | 等于。用于判断两个值是否相等。如果是给变童赋值,则使用"=” | |
!= | 不等于 | |
A~B | 判断字符串 A 中是否包含能匹配 B 表达式的子字符串 | |
A!~B | 判断字符串 A 中是否不包含能匹配 B 表达式的子字符串 | |
正则表达式 | /正则/ | 如果在“//”中可以写入字符,则也可以支持正则表达式 |
调用AWK的方式
1.命令行方式
awk [-F field-separator] 'commands' input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的文件。
在awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是'空格'。
2.shell脚本方式
将所有的awk命令插入一个文件,并使awk程序可执行,然后'awk命令解释器'作为脚本的首行,一遍通过键入脚本名称来调用。
相当于shell脚本首行的:#!/bin/sh
可以换成:#!/bin/awk
3.将所有的awk命令插入一个单独文件,然后调用:
awk -f awk-script-file input-file(s)
其中,-f选项加载awk-script-file中的awk脚本,input-file(s)跟上面的是一样的。
awk中的正则
<、<=、==、!=、>=、~匹配正则表达式(操作列)、!~不匹配正则表达式(取反)
匹配:awk '{if ($4~/ASIMA/) print $0}' temp 表示如果第四个域包含 ASIMA,就打印整条
精确匹配:awk '$3=="48" {print $0}' temp 只打印第 3 域等于"48"的记录
不匹配: awk '$0 !~ /ASIMA/' temp 打印整条不包含 ASIMA 的记录
不等于: awk '$1 != "asima"' temp
小于: awk '{if ($1<$2) print $1 "is smaller"}' temp
设置大小写: awk '/[Gg]reen/' temp 打印整条包含 Green,或者 green 的记录
任意字符: awk '$1 ~/^...a/' temp 打印第 1 域中第四个字符是 a 的记录,符号’^’代表行首,符合’.’代表任意字符
或关系匹配: awk '$0~/(abc)|(efg)/' temp 使用| 时,语句尽量括起来
并且关系匹配 awk -F: '$0~/(root)/ && $0~/(x)/'
AND 与关系: awk '{if ( $1=="a" && $2=="b" ) print $0}' temp
awk -F: '$3 == $4 {print $0}'
OR 或关系: awk '{if ($1=="a" || $1=="b") print $0}' temp
awk 的简单实用
awk '{print $1}' /etc/passwd
awk -F ':' '{print $1}' /etc/passwd #-F指定域分隔符为':'
awk -F ':' '{print $1"\t"$7}' /etc/passwd #拼接域
awk -F ':' '{$4=$3-$2; print $4}' #拼接域
awk -F: '/root/' /etc/passwd #过滤
awk -F: '/^root/' /etc/passwd
awk -F: '/root/{print $7}' /etc/passwd
awk -F: 'NR==3{print $7}' /etc/passwd
awk -F: 'NR>=3 && NR<=5{print $7}' /etc/passwd
awk -F: 'NR<=3 || NR>=5{print $7}' /etc/passwd
awk 'BEGIN{print "hh"} {print $1} END{print "hh"}'
# awk 中变量可以直接打印,不需要使用$
# awk中字符串的拼接使用" ",可以在引号内添加空格,也可以使用逗号来加空格
# awk中,正则要放到斜杠/ ,或者指定的边界内
awk 'BEGIN{FS=":";}/root/{print $0} ' a.txt
[root@egon day09]# awk 'BEGIN{FS=":";OFS="-"}{print NR,$0}' 6.txt
1-root:x:0:0:root:/root:/bin/bash
2-bin:x:1:1:bin:/bin:/sbin/nologin
3-daemon:x:2:2:root:/sbin:/sbin/nologin
4-adm:x:3:4:adm:/var/adm:/sbin/nologin
5-lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
[root@egon day09]# awk 'BEGIN{FS=":";OFS="-"}/root/{print NR,$1,$3,$5}' 6.txt
1-root-0-root
3-daemon-2-root
[root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $0} ' 6.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:root:/sbin:/sbin/nologin
[root@egon day09]# awk 'BEGIN{FS=":";}/root/{print NF} ' 6.txt
7
7
[root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $NF} ' 6.txt
/bin/bash
/sbin/nologin
[root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $(NF-1)} ' 6.txt
/root
/sbin
#会显示每列的内容
awk '(tot+=$3);END {print tot}' temp
awk '{(tot+=$3)};END {print tot}' temp
#文件长度相加
ls -l|awk '/^[^d]/ {print $9"\t"$5} {tot+=$5} END{print "totKB:" tot}'
#awk内,支持变量直接运算,相当于expr
如果只是显示/etc/passwd的账户和账户对应的shell,而账户与shell之间以逗号分割,而且在所有行添加列名name,shell,在最后一行添加"blue,/bin/nosh"。
cat /etc/passwd |awk -F ':' 'BEGIN {print "name,shell"} {print $1","$7} END {print "blue,/bin/nosh"}'
name,shell
root,/bin/bash
daemon,/bin/sh
bin,/bin/sh
sys,/bin/sh
....
blue,/bin/nosh
#awk工作流程是这样的:先执行BEGING,然后读取文件,读入有/n换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域,随后开始执行模式所对应的动作action。接着开始读入第二条记录······直到所有的记录都读完,最后执行END操作。
awk内置变量
awk有许多内置变量用来设置环境信息,这些变量可以被改变,下面给出了最常用的一些变量。
ARGC 命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行 -F选项
NF 浏览记录的域的个数,列数,列
NR 已读行的记录数,行号
OFS 输出域分隔符,默认是空格
ORS 输出记录分隔符
RS 控制记录分隔符
NR>=1 && NR<=3 相当于sed中的 1,3中的行号定位
; 分割多条命令
此外,$0变量是指整条记录。$1表示当前行的第一个域,$2表示当前行的第二个域,......以此类推。
统计/etc/passwd:文件名,每行的行号,每行的列数,对应的完整行内容:
#awk -F ':' '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd
filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash
[root@egon day09]# awk 'BEGIN{FS=":";}/root/{print $1,$3} ' 6.txt
root 0
daemon 2
[root@egon day09]# awk 'BEGIN{FS=":";}/root/{print "用户名:",$1,"用户id:",$3} ' 6.txt
用户名: root 用户id: 0
用户名: daemon 用户id: 2
使用printf替代print,可以让代码更加简洁,易读
awk -F ':' '{printf("filename:%10s,linenumber:%s,columns:%s,linecontent:%s\n",FILENAME,NR,NF,$0)}' /etc/passwd
[root@egon day09]# date | awk '{printf "年:%s\n月:%s\n",$NF,$2}'
年:2020
月:Sep
[root@egon day09]# awk 'BEGIN{FS=":";}/root/{printf "用户名:%s 用户id:%s\n",$1,$3} ' 6.txt
用户名:root 用户id:0
用户名:daemon 用户id:2
# %-15s 表示占位符,左对齐,站位15个空格
[root@egon day09]# awk -F: '{printf "用户名:%-15s|用户id:%-15s|用户组id:%-15s\n",$1,$3,$4}' /etc/passwd
[root@hass-11 ~]# df |awk 'NR>1{printf "对象:%s\t已用百分比:%s\n",$1,$5}'
对象:devtmpfs 已用百分比:0%
对象:tmpfs 已用百分比:0%
print和printf
awk中同时提供了print和printf两种打印输出的函数。
其中print函数的参数可以是'变量、数值或者字符串'。字符串必须用'双引号'引用,参数用'逗号分隔'。如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与'输出文件的分隔符'的作用是一样的,只是后者是空格而已。
printf函数,其用法和c语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。
定位
#正则表达式
[root@hass-11 ~]# awk '/root/{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk '/^root/{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk '$2 ~ /root/{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk '$2 !~ /root/{print NR,$0}' /etc/passwd
#比较表达式
[root@hass-11 ~]# awk 'NR>=3 && NR<=5{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk 'NR>=3 || NR<=5{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk -F: '$3 > 500{print $1,$3}' /etc/passwd
[root@hass-11 ~]# awk -F: '$1 !=root{print $1,$3}' /etc/passwd
#行号
[root@hass-11 ~]# awk 'NR=5{print NR,$0}' /etc/passwd
#幂数运算,+ - * / %(模) ^(幂2^3)
[root@hass-11 ~]# awk -F: '$4*12 >40{print $0}' /etc/passwd
[root@hass-11 ~]# awk -F: 'NR%2 == 1{print $0}' /etc/passwd #取奇数行
#且 或 非
[root@hass-11 ~]# awk 'NR>=3 && NR<=5{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk 'NR>=5 || NR<=3{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk '!(NR>=3 && NR<=4){print NR,$0}' a.txt
[root@hass-11 ~]# awk 'NR>=3,NR<=5{print NR,$0}' /etc/passwd #
[root@hass-11 ~]# awk 'NR>=3,/yy/{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk '/root/,NR<=5{print NR,$0}' /etc/passwd
[root@hass-11 ~]# awk '/root/,/yy/{print NR,$0}' /etc/passwd #可以多次匹配
#awk中,定位既可以在{}外,也可以在{}内,只不过在{}内要加if判断
awk 内置字符串函数
gsub(r,s) 在整个$0中用s替代r
awk 'gsub(/name/,"xingming") {print $0}' temp
gsub(r,s,t) 在整个t中用s替代r
index(s,t) 返回s中字符串t的第一位置
awk 'BEGIN {print index("Sunny","ny")}' temp 返回4
length(s) 返回s的长度
match(s,r) 测试s是否包含匹配r的字符串
awk '$1=="J.Lulu" {print match($1,"u")}' temp 返回4
split(s,a,fs) 在fs上将s分成序列a
awk 'BEGIN {print split("12#345#6789",myarray,"#")"'
返回3,同时myarray[1]="12", myarray[2]="345", myarray[3]="6789"
sprint(fmt,exp) 返回经fmt格式化后的exp
sub(r,s) 从$0中最左边最长的子串中用s代替r(只更换第一遇到的匹配字符串)
substr(s,p) 返回字符串s中从p开始的后缀部分
substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部分
awk编程
变量和赋值
除了awk的内置变量,awk还可以自定义变量。
下面统计/etc/passwd的账户人数
awk '{count++;print $0;} END{print "user count is ", count}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
......
user count is 40
#count是自定义变量。其实print只是一个语句,多个语句,以;号隔开。
这里没有初始化count,虽然默认是0,但是妥当的做法还是初始化为0:
awk 'BEGIN {count=0;print "[start]user count is ", count} {count=count+1;print $0;} END{print "[end]user count is ", count}' /etc/passwd
[start]user count is 0
root:x:0:0:root:/root:/bin/bash
...
[end]user count is 40
统计某个文件夹下的文件占用的字节数
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}'
[end]size is 8657198
如果以M为单位显示:
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}'
[end]size is 8.25889 M
#注意,统计不包括文件夹的子目录。
条件语句
awk中的条件语句是从C语言中借鉴来的,见如下声明方式:
if (expression) {
statement;
statement;
... ...
}
if (expression) {
statement;
} else {
statement2;
}
if (expression) {
statement1;
} else if (expression1) {
statement2;
} else {
statement3;
}
# 1、单分支
[root@egon day10]# awk -F: '$3 > 1000{print $0}' /etc/passwd
awk -F: '{if ($3>1000) {count++;}}END{print count}' /etc/passwd
# 2、双分支
awk -F: '{if ($3==0) {x++} else {y++}}END{printf "管理用户个数:%s 其他用户个数:%s\n",x,y}' /etc/passwd
# 3、多分支
awk -F: '{if ($3==0) {x++} else if($3>1000) {y++} else {z++}}END{printf "管理用户个数:%s 普通用户个数:%s 系统用户:%s\n",x,y,z}' /etc/passwd
#awk中判断结束不需要使用fi 结尾
#awk中,{}可以嵌套
#分号表示不同命令的分割
统计某个文件夹下的文件占用的字节数,过滤不是4096大小的文件(一般都是文件夹):
ls -l |awk 'BEGIN {size=0;print "[start]size is ", size} {if($5!=4096){size=size+$5;}} END{print "[end]size is ", size/1024/1024,"M"}'
[end]size is 8.22339 M
循环语句
awk中的循环语句同样借鉴于C语言,支持while、do/while、for、break、continue,这些关键字的语义和C语言中的语义完全相同。
#循环
[root@hass-11 ~]# awk -F: '/root/{count=0;while(count < 3) {count++;print $0}}' /etc/passwd
[root@hass-11 ~]# awk -F: '/root/{for(i=0;i<3;i++) {print $0}}' /etc/passwd
#循环+判断+continue
[root@hass-11 ~]# awk -F: '/root/{count=0;while(count < 3) {count++;if (count==1) {continue};print $0}}' /etc/passwd
[root@hass-11 ~]# awk -F: '/root/{for(i=0;i<3;i++) {if(i==1) {continue};print $0}}' /etc/passwd
#循环+判断+break
[root@hass-11 ~]# awk -F: '{count=0;while(count < 3) {count++;if (count==2) {break};print $0}}' /etc/passwd
[root@hass-11 ~]# awk -F: '/root/{for(i=0;i<3;i++) {if(i==1) {break};print $0}}' /etc/passwd
#死循环
[root@hass-11 ~]# awk -F: '{while(1 !=2) {print $0}}' /etc/passwd
#计算的话,count一定要定义在 BEGIN
#循环的话,count一定要定义在 第二段
# xxx=0,在awk中可以省略,直接使用
数组
因为awk中数组的下标可以是数字和字母,数组的下标通常被称为关键字(key)。值和关键字都存储在内部的一张针对key/value应用hash的表格里。由于hash不是顺序存储,因此在显示数组内容时会发现,它们并不是按照你预料的顺序显示出来的。数组和变量一样,都是在使用时自动创建的,awk也同样会自动判断其存储的是数字还是字符串。一般而言,awk中的数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。
显示/etc/passwd的账户
#普通数组的定义和遍历
awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd
0 root
1 daemon
2 bin
3 sys
4 sync
5 games
......
#name[0] 表示数组中的第一个元素
#awk定义的数组,在命令行中看不到
数组+for循环的另外一种格式
awk -F: 'BEGIN{i=0} {user[i++]=$1}END{print user[0]}' /etc/passwd
awk -F: '{user[i++]=$1}END{print user[0]}' /etc/passwd
遍历数组
# 有序遍历
awk -F: 'BEGIN{i=0} {user[i++]=$1} END{for(j=0;j<NR;j++) {print j,user[j]}}' /etc/passwd
awk -F: 'BEGIN{i=1} {user[i++]=$1} END{for(j=1;j<=NR;j++) {print j,user[j]}}' /etc/passwd
for ((i=0;i<28;i++)) #固定格式,对应user0 user1 user2
do
echo ${user[$i]}
done
# 无序遍历
awk -F: 'BEGIN{i=1}{user[i++]=$1} END{for (x in user) {print user[x]}}' /etc/passwd
awk -F: 'BEGIN{i=0}{user[$1]=$3} END{for (k in user) {print k,user[k]}}' /etc/passwd
#普通数组既可以有序遍历,又可以无序遍历
#关联数组只能无序遍历
脚本中的awk
!/bin/awk -f
# all comment lines must start with a hash '#'
# name: student_tot.awk
# to call: student_tot.awk grade.txt
# prints total and average of club student points
# print a header first
BEGIN
{
print "Student Date Member No. Grade Age Points Max"
print "Name Joined Gained Point Available"
print"========================================================="
}
# let's add the scores of points gained
(tot+=$6);
# finished processing now let's print the total and average point
END
{
print "Club student total points :" tot
print "Average Club Student points :" tot/N
}
使用pid文件限制重复启动进程
[root@egon day10]# cat proc.sh
#!/bin/bash
pidfile='/tmp/proc.pid'
if [ -f $pidfile ];then
pid=`cat $pidfile`
ps $pid &>/dev/null
[ $? -eq 0 ] && echo "脚本正在运行..." && exit 1
fi
echo "$$" > $pidfile
echo "脚本正在运行。。。"
sleep 500
echo "脚本运行完毕。。。"
rm -rf $pidfile
shell队列实现并发控制
https://blog.51cto.com/egon09/1754317
awk编程的内容极多,这里只罗列简单常用的用法,更多请参考 http://www.gnu.org/software/gawk/manual/gawk.html