linux 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

操作符
 赋值
  = += -= /= *=
 逻辑与 逻辑或 逻辑非
  && || !
 匹配正则或不匹配,正则需要用 /正则/ 包围住
  ~  !~
 关系 比较字符串时要把字符串用双引号引起来
  < <= > >= != ==
 字段引用
  $ 字段引用需要加$,而变量引用直接用变量名取
 运算符
  + - * / % ++ --
 转义序列
  \ 自身
  $ 转义$
   制表符
   退格符
   回车符
   换行符
  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" "$3" "var}' /etc/passwd
 # awk -F: '{print ($3>500?$1:$2)}' /etc/passwd

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

输出样式
 %s是字符类型,%d数值类型
 printf默认是不输出换行的所以要加
 10和7是偏移量
 默认是右对齐,所有加个- 就是左对齐,就是把不足的位数用空格填充
 注意:格式与输出列之间要有逗号
 # awk -F: '{printf "%-10s %-10d %s ",$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 " "}' /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" "$1}'
0 root
[root@tch254 ~]#

#awk -F: 'BEGIN {while (getline < "/etc/passwd" > 0) print $3" "$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 ~]#

ls -l -t |grep txt | head -n 3 | tail -n 1| awk -F' ' '{print $NF}' | xargs rm -f

删除文件夹里面倒数第二新的log文件

ls命令的-t ,让文件按照时间排序,

 
head -n 2 | tail -n 1| 取文本的第二行
 
awk -F' ' '{print $NF}' 打印一行中最后的一列
 
xargs 命令也是比较有意思的,把输入传给另外一个命令作为参数。
原文地址:https://www.cnblogs.com/xiaotlili/p/3210586.html