AWK —— 三个作者的姓的首字母


awk很强大,是编程语言。

流编辑器、非交互式的文本编辑器、报告生成工具(对文本进行排版,数据汇总)


awk的版本

awk       gawk      nawk

旧版的   GNU awk   新版awk(Solaris)

   # ll /usr/bin/awk 

lrwxrwxrwx. 1 root root 14 May 23 17:03 /usr/bin/awk -> ../../bin/gawk

   # ll /usr/bin/gawk 

lrwxrwxrwx. 1 root root 14 May 23 17:03 /usr/bin/gawk -> ../../bin/gawk

   # ll /bin/gawk 

-rwxr-xr-x 1 root root 382456 Jul  4  2012 /bin/gawk

     在我们的系统里,awk和gawk一样


工作模式:

   1、行工作模式,每次读入一行,存到"$0"里

   2、使用内置变量FS(区域分隔符,默认是空白)分隔这行,存到$1-$100

   3、输出时使用内置变量OFS(输出字段分隔符,默认也是空白)

   4、继续处理下一行


  假设this is a test  这样的行

      $0:this is a test

      $1: this

      $2: is

      $3: a

      $4: test

   最多就到$100



一、awk的语法

  awk [选项] '/正则表达式/{处理动作1;处理动作2;...;处理动作n}' 文件列表


二、例子

  打印员工名字和生日

    # awk '{print $1,$4}' employees

 

  打印/etc/passwd文件的用户名和GID

    # awk -F: '{print $1,$4}' /etc/passwd

三、基本操作

  1、设定分隔符

     -F:指定分隔符

         分隔符可以是字母、符号、数字和"空"

    可以同时指定多个分隔符

    默认是空白(空格或者tab)

     1)单个字符做分隔符

# awk -F: '{print $1}' awk.pass

     2)复合分隔符,多个字符组成的分隔符

# awk -F ":/" '{print $1,$2}' awk.pass

     3)同时指定多个分隔符

 # awk -F "[:/]" '{print $1,$7}' awk.pass

  2、awk的输出

     1)print

  打印的内容可以是文件的内容,也可以是和文件不相关的内容

        # awk '{print "hello"}' awk.pass

        \n:换行符

          在每一行下面添加空行

           # awk '{print $0"\n"}' awk.pass

        # awk -F: '{print "user:"$1}' awk.pass  //字符串输出要加双引号

   \t:制表符,tab

  # awk -F: '{print $1"\t"$2}' awk.pass

           # awk -F: '/root/{print $1,$3}' awk.pass 

        练习:

  ifconfig和awk结合,取出eth0的IP地址

   # ifconfig eth0 | awk -F"[: ]" '/Bcast/{print $13}'

     # ifconfig eth0| awk -F: '/Bcast/{print $2}' | awk '{print $1}'



       2)printf  —— 可以格式化输出,默认不输出换行符

   printf format,item1,item2,...,itemn

   format:指示符都以%开头,后面一个字符:

    %s:表示字符串  string

    %d:表示十进制整数,没有四舍五入,直接取整

    %f:表示浮点数,即小数,默认小数点后保留六位,会四舍五入

    %%: 表示%本身

    %o: 八进制数

    %x: 十六进制数

    %c:  字符

 修饰符:

N(数字),指定显示宽度   %2s 

  注意:宽度>=字段的最长的长度

-:表示左对齐(默认是右对齐)   %-2s

+: 显示数值符号

# echo "12.35 11.2345 34.56" > awkf1

# awk '{printf "%d",$1}' awkf1   

# awk '{printf "%d\n",$1}' awkf1

# awk '{printf "%f\n",$1}' awkf1 

   12.350000

# awk '{printf "%.1f\n",$1}' awkf1 

    12.3

# awk '{printf "%5.2f\n",$2}' awkf1 

   5.2表示显示宽度是5,即整数位和小数位的总显示长度为5;2表示保留两位小数

   \n:换行符

printf与print的区别:

1)printf需要指定格式

2)printf默认不换行

3)printf默认没有输出分隔符

默认输出时,右对齐

# awk -F: '{printf "%10s %d\n",$1,$3}' awk.pass

左对齐

# awk -F: '{printf "%-10s %d\n",$1,$3}' awk.pass

 

 练习:

        /etc/passwd文件,让你格式化输出用户名,UID,GID三列

  #  awk -F: '{printf "%-13s\t%d\t%d\n",$1,$3,$4}' /etc/passwd

  # awk -F: '{printf "%-15s %-10d %-10d \n",$1,$3,$4}'  /etc/passwd

# awk -F: '{printf "%-15s %d %d\n",$1,$3,$4}'  /etc/passwd

  3、awk的操作符

     1)算数运算符

   + - * / 

  %    取余  5%3 = 2

        x^y x**y   x的y次方,x的y次幂   2^5 = 32

     # awk '{print $3+$5}' employees

     # awk '{print $3-$5}' employees

     # awk '{print $3*$5}' employees

     # awk '{print $5/$3}' employees

     # awk '{print $5%$3}' employees

     2)关系运算符

(1)数值之间的关系

    >   <   >=  <=  !=  ==

   $3 == 0

   $1 == "root"

(2)字符串之间的关系

   x ~ /y/   —— 匹配正则

   x !~ /y/  —— 不匹配

打印uid大于3的用户名和uid

   # awk -F: '$3 > 3 {print $1,$3}' awk.pass

用户名包含a的,输出其名字

   # awk -F: '$1 ~ /a/ {print $1}' awk.pass

     3)逻辑运算符

&&   ||    !

打印uid大于等于3,并且用户名中含有p的用户名和家目录

# awk -F: '$3 >= 3 && $1 ~ /p/ {print $1,$6}' /etc/passwd

打印用户名中不含p的行

# awk -F: '!($1 ~ /p/){print}' awk.pass

   练习:/etc/passwd

1、打印GID在20和30之间的用户名和登录shell

  # awk -F : '$4 >= 20 && $4 <= 30 {print $1,$7}' /etc/passwd

2、打印uid和gid不相等的用户名、uid和gid

  # awk -F:  '$3!=$4  {print $1,$3,$4 }' /etc/passwd

3、将uid和用户名互换位置输出,其他位置原样输出

  # awk -F: '{print $3,$2,$1,$4,$5,$6,$7}' /etc/passwd

4、打印shell为非登录shell的用户名

  # awk -F :  '!($7 ~ /sh/) {print $1}' /etc/passwd

  # awk -F: '$7 !~ /sh/{print $1}' /etc/passwd

5、打印所属组是bin的用户名  

  # awk -F: '$4==1 {print $1}' /etc/passwd

6、打印100以内能被7整除的数以及包含7的数(seq 100 生成1-100)

  # seq 100 | awk   '$1~/7/ || $1%7==0{print}'

 -----------------------------------------------------------------


    4)赋值运算符

=   +=   -+  *=  /=   %=  ++   --    ^=   **=    

4、定址(模式匹配)——awk+正则表达式

   常见的模式:

     1)没有模式,每一行都处理

# awk -F: '{print $1}' awk.pass

     2)有一个模式,处理匹配到模式的行

   # awk -F: '/^root/{print $1}' awk.pass

     3)范围定址   //,//   注意:不能使用行号定址

        # awk -F: '/^r/,/^d/{print $1}' awk.pass

        范围定址仍然是最小匹配


5、awk的变量

   内部变量、自定义变量

   1)内部变量

      (1)$0    表示一整行内容

           # awk -F: '/root/{print $0}' awk.pass 

  # awk -F: '/root/{print}' awk.pass 

  # awk -F: '/root/' awk.pass

      (2)$1 -  $100

    $1: 第一列

    $2:第二列

  ......

  $100:第100列

      (3)与记录相关的变量   记录=行

  FS:Field Separator 字段分隔符,默认是空白;其实是读取文本时,使用的字段分隔符   FS=":"

  RS:Record Separator 记录分隔符,即行的分隔符,默认是换行符;其实就是读取文本时,使用的行分隔符  RS="/"

  OFS:Output Field Separator 输出字段分隔符

  ORS:Output Record Separator 输出记录分隔符

       (4)与数据相关的变量

      NR:记录数  awk处理的记录的总数,可以表示行号

           NF:字段数  当前记录的字段数

$NF:当前行最后一个字段的值

$(NF-1):当前行的倒数第二个字段 

    打印/etc/passwd的第二行

# awk 'NR==2 {print}' /etc/passwd

    打印偶数行

awk 'NR%2==0 {print}' /etc/passwd

    打印/etc/passwd同时显示行号

# awk '{print NR,$0}' /etc/passwd

  # awk -F: '{print NR": "NF}' awk.pass

  # awk -F: '{print NR": "NF,$(NF-1)}' awk.pass

练习:

  通过mount命令,查看所有分区是否可读可写,输出结果为:

      /dev/sda2   rw

      wp:# mount | awk -F "[ ()]" '/^\//{print $1,$7}'

   2)自定义变量

用户根据自己需求去定义的变量

变量的命名规则:只能使用字母、数字、下划线,并且不能以数字开头,在awk中变量名是区分大小写的

      合法的:a1  a  abcd  ab_123   

     不合法的:1a   1_a

什么是变量?

  保存值的。在awk中变量可以先定义再使用,也可以直接就用。

说明:如果没有对变量进行定义没有初始值,如果变量被认为是字符串,它的值就是空"";如果字符串被认为是数值,那么它的值就是0。

给变量赋值:var=value

  变量名=数字

  变量名="字符串"

# awk '{a="qwe";print a}' awk.pass

     练习:

        自己定义两个变量,一个数值型,一个字符串型,使用printf打印它们

# awk '{a="Hello";b=100;printf"%s\t%d\n",a,b}' awk.pass

# awk '{a="hello";b=250;printf "%s %d\n",a,b}' awk.pass

    赋值运算:

      = += ......

      a=5

      a+=5    a=a+5

a-=5    a=a-5

a*=5    a=a*5

    a++  a--    //先使用,后运算

    ++a  --a    //先运算,再使用

    自增 自减     

       # awk '{a=5;b=a++;print a,b}' test

  6 5

       # awk '{a=5;b=a;a=a+1;print a,b}' test

  6 5

       # awk '{a=5;b=++a;print a,b}' a

  6 6

       # awk '{a=5;a=a+1;b=a;print a,b}' test

  6 6

       自己练习:

           --a

# awk '{a=5;b=--a;print a,b}' test

  4 4

    # awk '{a=5;a=a-1;b=a;print a,b}' test

  4 4

6、awk中的重定向   了解

   >    >>

   # awk -F: '$3 > 2 {print $1,$3 > "/tmp/pass"} ' awk.pass 

   # cat /tmp/pass 

adm 3

lp 4

   # awk -F: '$3 > 2 {print $1,$3 >> "/tmp/pass"} ' awk.pass 

   # cat /tmp/pass 

adm 3

lp 4

adm 3

lp 4

   # awk -F: '$3 > 2 {print $1,$3 } ' awk.pass  >> /tmp/pass

7、管道   了解

   # awk -F: '{print $1,$3 | "sort -nr -k2"}' awk.pass

     默认管道是不会关闭的,所以在脚本真正使用时候是需要关闭的


8、awk的特殊模式  BEGIN  END

   '

   BEGIN{动作}  //在处理文件之前执行的,并且只执行一次 

   /定址/{动作} //有可能会执行多次

   END{动作}    //在处理文件之后执行的,并且只执行一次

   '

  BEGIN:一般定义变量,初始化分隔符,定义数组,打印表头等等

  END:一般END数据汇总,打印结尾


   注意:BEGIN和END可以单独存在


   # awk 'BEGIN{FS=":"}{print $1,$2}END{print "over"}' awk.pass 

root x

bin x

daemon x

adm x

lp x

over

  # awk 'BEGIN{print "class is not over"}'  //只有BEGIN的时候,后面可以没有文件

class is not over

 

  END:后面必须接文件,并且这个文件要么为空文件,要么为已存在的文件

  # awk 'END{print "class is not over"}' awk.pass 

class is not over

  # touch abc

  # awk 'END{print "class is not over"}' abc

class is not over

  # awk 'BEGIN{sum=0}{sum+=$3;print $1,$3}END{print "sum: "sum}' employees

Tom 4424

Mary 5346

Sally 1654

Billy 1683

sum: 13107


  练习:统计/etc目录下所有普通文件总大小

# ll /etc/ | awk '/^-/{sum+=$5}END{print sum}'

# ll /etc/ | awk 'BEGIN{sum=0}/^-/{sum+=$5}END{print sum}'


9、awk脚本

   打印datafile文件的第一个字段和倒数第二个字段,对最后一个字段求和并打印

   # cat sc.awk 

BEGIN{      //注意:大括号一定要跟BEGIN同行

     print "Class is over.That is not true."

}

{

     printf "%-10s %d\n",$1,$(NF-1);sum+=$NF

}

END {

     printf "sum:%10.2f\n",sum

}