ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

三剑客 -- awk

2020-09-05 18:31:34  阅读:252  来源: 互联网

标签:etc -- passwd awk print NR root 三剑客


[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

https://blog.51cto.com/egon09

标签:etc,--,passwd,awk,print,NR,root,三剑客
来源: https://www.cnblogs.com/syy1757528181/p/13619145.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有