AWK详解

awk
语法
    awk [options] 'commands' files
option
-F 定义字段分隔符,默认的分隔符是连续的空格或制表符
  使用option中的-F参数定义间隔符号
  用$1,$2,$3等的顺序表示files中每行以间隔符号分隔的各列不同域
  NF变量表示当前记录的字段数
-v 定义变量并赋值 也可以借用次方式从shell变量中引入


command
读前处理 行处理 读后处理
1.读前处理 BEGIN{awk_cmd1;awk_cmd2}
2.行处理:定址 命令 
  定址方法: 正则,变量,比较和关系运算
正则需要用//包围起来 
  ^ 行首
  $ 行尾
  . 除了换行符以外的任意单个字符
  * 前导字符的零个或多个
  .* 所有字符
  [] 字符组内的任一字符
  [^] 对字符组内的每个字符取反(不匹配字符组内的每个字符)
  ^[^] 非字符组内的字符开头的行
  [a-z] 小写字母
  [A-Z] 大写字母
  [a-Z] 小写和大写字母
  [0-9] 数字
  \< 单词头 单词一般以空格或特殊字符做分隔,连续的字符串被当做单词
  \> 单词尾


扩展正则 加 -r 参数 或转义
sed -n '/roo\?/p' /etc/passwd  
sed -rn '/roo?/p' /etc/passwd
  ? 前导字符零个或一个
+ 前导字符一个或多个
  abc|def abc或def
  a(bc|de)f abcf 或 adef
  x\{m\}   x出现m次
  x\{m,\}  x出现m次至多次(至少m次)
  x\{m,n\} x出现m次至n次


NR变量定址 
            NR 表示AWK读入的行数
    FNR表示读入行所在文件中的行数
# awk '{print NR,FNR,$1}' file1  file2 
1 1 aaaaa
2 2 bbbbb
3 3 ccccc
4 1 dddddd
5 2 eeeeee
6 3 ffffff
#
        逻辑运算 可直接引用域进行运算
== >= <= != > < ~ !~
# awk 'NR==1 {print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

3.命令 {print $0}
4.读后处理 END {awk_cmd1;awk_cmd2;}




AWK变量
NR    当前记录的个数(全部文件连接后的统计) 
FNR   当前记录的个数(仅为当前文件的统计,非全部)
FS    字段分隔符 默认为连续空格或制表符,可以使用多个不同的符号做分隔符 -F[:/]
OFS   输出字符的分隔符 默认是空格
# awk -F: 'OFS="=====" {print $1,$2}' /etc/passwd
root=====x
NF    当前读入行的字段个数
ORS   输出记录分隔符 默认是换行
# awk -F: 'ORS="=====" {print $1,$2}' /etc/passwd
root x=====bin x=====
FILENAME 当前文件名


引用shell变量的方法
# a=root
# awk -v var=$a -F: '$1 == var {print $0}' /etc/passwd
或者 把整个命令拆开传递,让shell变量外露,
# awk -F: '$1 == "'$a'" {print $0}' /etc/passwd
# a=NF
# awk -F: '{print $'$a'}' /etc/passwd


操作符
赋值
= += -= /= *= 
逻辑与 逻辑或 逻辑非
&& || !
匹配正则或不匹配,正则需要用 /正则/ 包围住
~  !~
关系 比较字符串时要把字符串用双引号引起来
< <= > >= != ==
字段引用
$ 字段引用需要加$,而变量引用直接用变量名取
运算符
+ - * / % ++ --
转义序列
\\ \自身
\$ 转义$
\t 制表符
\b 退格符
\r 回车符
\n 换行符
\c 取消换行




试做
    打印uid在30~40范围内的用户名。
    打印第5-10行的行号和用户名
    打印奇数行
    打印偶数行
    打印字段数大于5的行
    打印UID不等于GID的用户名
    打印没有指定shell的用户
    打印1..1000以内的7的倍数和包含7的数
    
    


流程控制
分支结构


if (条件) 动作
若有多个动作,则要用大括号将动作体包含起来  if (条件) {动作1;动作2}
# awk -F: '{if ($1 == "root") print $1}' /etc/passwd
root

# awk -F: '{if ($1 == "root") {print $1;print $6}}' /etc/passwd
root
/root
#
 
if (条件1)
动作1
else
动作2
# awk -F: '{if ($1 == "root"){print $1}else print $6}' /etc/passwd
# awk -F: '{if ($1 == "root") print $1;else print $6}' /etc/passwd
上面两个命令是等价的,要么用分号隔开,表示第一个动作体的结束,要么将动作体用大括号定位范围

if (条件 1)
动作1
else if(条件 2)
动作2
else if(条件 3)
动作3
else
动作4
# awk -F: '{if ($1 == "root") print $1;else if ($1 == "seker") print $6;else if ($1 == "zorro") print $7;else print NR}' /etc/passwd
root
2
3
...
33
/home/seker
/bin/bash
36


条件 ? 动作1 : 动作2
expr?action1:action2
# awk -F: 'var=($3 >= 500)?$1:"system_user" {print $1"\t"$3"\t"var}' /etc/passwd
# awk -F: '{print ($3>500?$1:$2)}' /etc/passwd


试做
   将系统用户按UID分组标记 0 admin; 1-499 sysuser; 500+ users


输出样式
%s是字符类型,%d数值类型
printf默认是不输出换行的所以要加\n
10和7是偏移量
默认是右对齐,所有加个- 就是左对齐,就是把不足的位数用空格填充
注意:格式与输出列之间要有逗号
# awk -F: '{printf "%-10s %-10d %s\n",$1,$3,$7}' /etc/passwd 




读前处理和读后处理
# awk -F: 'BEGIN{i=1} {i++} END {print i}' /etc/passwd
47
#
# awk -F: 'BEGIN {print NR,NF}' /etc/passwd
0 0

# awk -F: 'END {print NR,NF}' /etc/passwd
46 7
#
试做
    找出普通用户的用户名并统计数量 
# awk -F: 'BEGIN{i=0} $3 >= 500 {print $1;i++} END {print i}' /etc/passwd
    计算UID相加的总和;计算GID相加的总和
    计算VSZ和RSS各自的和 并以M单位显示




循环语句
  while(条件) {
动作
条件运算
  }
# awk -F: '{while($3<3) {print $3,$1;$3++}}' /etc/passwd
0 root
1 root
2 root
1 bin
2 bin
2 daemon

BEGIN块可以独立使用,不需要引入文件
# awk 'BEGIN{i=1;while(i<100) {print i;i++}}'
试做
    打印100以内的偶数
    # awk 'BEGIN{i=1;while(i<100) {if (i%2==0) print i;i++}}'

x=1
do {
动作1
x++
  } while (x<5)
# awk 'BEGIN{i=5;do{print i;i++}while(i<10)}'
# awk 'BEGIN{i=5;do{print i;i++}while(i<1)}'



for(预置;条件;递增) {
动作
  }
# awk 'BEGIN {for (x=1;x<=4;x++) print x }'
1
2
3
4
#
    # awk 'BEGIN{for (i=1;i<=4;i++) {for (j=1;j<=4;j++) print i,j}}'


 
试做
    使用嵌套的for循环,打印100-999之间的数,个十百位分别用一个for来打印
# awk 'BEGIN{OFS="";for (i=1;i<=9;i++) {for (j=0;j<=9;j++) {for (n=0;n<=9;n++) print i,j,n}}}'
    打印乘法口诀表
# cat 99.sh 
#!/bin/bash
awk 'BEGIN{
for(i=1;i<10;i++)
{
for(j=1;j<=i;j++)
printf "%d*%d=%d ",j,i,j*i
print
}


}'



    打印金字塔
# cat jin.sh 
#!/bin/bash
awk 'BEGIN{
num=5
for(i=1;i<=num;i++)
{
for (n=1;n<=num-i;n++)
printf "%s"," "
for (j=1;j<=2*i-1;j++)
    printf "%s","*"
print
}
}'



    逆序输出每个字段
达到这样既可
/bin/bash
/root
root
0
0
x
root




# awk -F: '{for (x=NF;x>0;x--) print $x}' /etc/passwd 


继续解决上一个试做题的格式问题
# awk -F: '/bash$/{for (x=NF;x>0;x--) printf "%-13s",$x;printf "\n"}' /etc/passwd


跳转语句
break 跳出循环
# awk 'BEGIN {for(x=1;x<5;x++) {if (x==3) break;print x }}'
1
2

continue 在达到循环底部之前终止当前循环 从新开始下一次循环
# awk 'BEGIN {for(x=1;x<5;x++) {if (x==3) continue;print x }}'
1
2
4


next 读入下一行 同时返回脚本顶部 这样可以避免对当前行执行其他操作
# awk -F: 'NR > 5 {next} {print $1} END {print NR}' /etc/passwd
root
bin
daemon
adm
lp
46

exit 使读取动作终止 并将控制移动到END,如果没有END则终止脚本
# awk -F: 'NR > 5 {exit} {print $1} END {print NR}' /etc/passwd
root
bin
daemon
adm
lp
6
#


数组
自定义数组
# awk 'BEGIN {ary[1]="seker";ary[2]="zorro";print ary[1],ary[2]}'
seker zorro
#
# awk 'BEGIN {ary[1]="seker";ary[2]="zorro";for(i in ary) print ary[i]}'
seker
zorro
#
删除一个元素 对元素给空值并不能清除这个元素 要想清除一个元素需要使用delete ary[idx]
# awk 'BEGIN {ary[1]="seker";ary[2]="zorro";ary[3]="blues";ary[2]="";for(i in ary) print ary[i]}'
seker


blues
# awk 'BEGIN {ary[1]="seker";ary[2]="zorro";ary[3]="blues";delete ary[2];for(i in ary) print ary[i]}'
seker
blues
#


循环产生数组和取出数组
# awk 'BEGIN{n=5;for (i=1;i<=n;i++) ary[i]=i+100;for(m in ary) print m,ary[m]}'
4 104
5 105
1 101
2 102
3 103


# awk -F: '{ary[NR]=$1} END {for(i in ary) print i,ary[i]}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
6 sync
7 shutdown
8 halt
9 mail
# awk -F: '{ary[$3]=$1} END {for(i in ary) print i,ary[i]}' /etc/passwd
10 uucp
11 operator
12 games
13 gopher
14 ftp
32 rpc
37 rpm


ARGV  命令行中参数数组
# awk '{for (i in ARGV) {print i,ARGV[i]}}'  /etc/passwd /etc/fstab
0 awk
1 /etc/passwd
2 /etc/fstab
#### i 为下标; ARGV[i] 下标为i的值
试做
    统计每种shell被使用的次数




函数


算术函数 int
[root@stu254 ~]# awk 'BEGIN {print int(3.9415)}'
3
[root@stu254 ~]# 




随机数函数 rand()  srand()
rand() 取值 0 > r < 1 之间 默认的种子是系统时间 精确到秒
srand()取值 0 > r < 1 之间 可以指定种子来影响rand()取值数 默认是系统时间 精确到秒


[root@stu254 ~]# awk 'BEGIN {srand(222);print int(rand()*100000000)}'
90204196
[root@stu254 ~]# 


字符串函数
substr(s,x[,y])
返回字符串s中从位置x起至y的子串,如果没有给出y,则从x开始到结束.
[root@stu254 ~]# awk 'BEGIN {x="abcdefxyz";print substr(x,4,3)}'
def
[root@stu254 ~]# 


大写小写
sprintf() 本身并不能打印,做格式转换,将数字转换成ASCII字符
# awk 'BEGIN {for(i=97;i<=122;++i) print tolower(toupper(sprintf("%c",i)))}'


字符串长度
length()  如果没有给定字符串则使用$0
[root@stu254 ~]# awk 'BEGIN {print length("abcdefxyz")}'
9


gsub(/abc/,"ABC",x) 全局字符串替换
从x中用匹配的abc正则替换成ABC 
[root@stu254 ~]# awk 'BEGIN {x="xyzabcxyzabcxyz";gsub(/abc/,"ABC",x);print x}'
xyzABCxyzABCxyz
[root@stu254 ~]#  sub 第一次的替换
[root@stu254 ~]# awk 'BEGIN {x="xyzabcxyzabcxyz";sub(/abc/,"ABC",x);print x}'
xyzABCxyzabcxyz
[root@stu254 ~]# 


       gensub(r, s, h [, t])   Search the target string t for matches of the reg-
                               ular  expression  r.   If  h is a string beginning
                               with g or G, then replace all matches of r with s.
                               Otherwise, h is a number indicating which match of
                               r to replace.  If t is not supplied,  $0  is  used
                               instead.
gensub(正则,替换,范围,目标串)
[root@tch254 ~]# awk 'BEGIN{print gensub("zorro","AAAA","2","seker zorro zorro seker")}'
seker zorro AAAA seker
[root@tch254 ~]# echo seker zorro zorro seker | sed 's/zorro/AAAA/2'
seker zorro AAAA seker
[root@tch254 ~]# 
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","g");print}'
seker AAAA AAAA seker
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","2");print}'
seker zorro AAAA seker
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","h");print}'
seker AAAA zorro seker
[root@tch254 ~]# echo seker zorro zorro seker | awk '{$0=gensub("zorro","AAAA","1");print}'
seker AAAA zorro seker
[root@tch254 ~]# 




系统函数
getline  


交互输入
[root@stu254 ~]# awk -F: 'BEGIN {printf "Enter Number: ";getline ;for(i=1;i<=$0;i++) print i}'
Enter Number: 3
1
2
3
[root@stu254 ~]# 


将输入赋值给变量
[root@stu254 ~]# awk -F: 'BEGIN {printf "Enter Number: ";getline NUM;for(i=1;i<=NUM;i++) print i}'
Enter Number: 3
1
2
3
[root@stu254 ~]# 


从文件中读入
[root@tch254 ~]# awk -F: 'BEGIN {getline < "/etc/passwd" ; print $3"\t"$1}'
0 root
[root@tch254 ~]# 


#awk -F: 'BEGIN {while (getline < "/etc/passwd" > 0) print $3"\t"$1}'


   getline < "/etc/passwd" 从文件中读入,每次读取一行,默认情况下读取的次数等于awk自身引入文件的行数
   也可以放到for中来控制读取的次数
    > 0    测试读取的返回值,成功返回1,失败返回-1,0文件末尾


从命令输出中输入
[root@stu254 ~]# awk 'BEGIN {"uname -a"|getline ;print $3}'
2.6.18-53.el5
[root@stu254 ~]# 








system(command)
系统命令要用""引起来
[root@stu254 ~]# rm -rf abc/
[root@stu254 ~]# awk 'BEGIN {if(system("mkdir abc") != 0 ) print "ERR"}'
[root@stu254 ~]# awk 'BEGIN {if(system("mkdir abc") != 0 ) print "ERR"}'
mkdir: 无法创建目录 “abc”: 文件已存在
ERR
[root@stu254 ~]#
[root@tch254 ~]# awk 'BEGIN {if(system("mkdir abc 2>/dev/null") != 0 ) print "ERR"}'
ERR
[root@tch254 ~]#  


awk脚本的介绍 -f 与 #!/bin/awk -f


使用awk添加系统用户
[root@mail ~]# cat useradd.awk 
#!/bin/awk -f


{
system("useradd "$1";echo "$2"|passwd --stdin "$1)
}
[root@mail ~]# cat username 
myname 1234
[root@mail ~]# 
[root@mail ~]# ./useradd.awk ./username 
Changing password for user myname.
passwd: all authentication tokens updated successfully.
[root@mail ~]#
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值