一:awk为报告生成器


Linux之awk_awk

AWK字段:awk对每行文档按照分隔符(缺省为空格指定的话为-F)把行内的内容切成每个字段。

$0:代表整行。

$1...$N:代表被分割符分开的每个字段。


二:awk基本语法

基本语法: awk [options] 'program' file file ...

          awk [options] 'PATTERN{action}' file file ...


输入选项 -F  指定分割符选项。

       

     
awk -F : '{print  $1 ,$7}'  /etc/passwd  #指定以冒号作为字段分割符。默认是以空格



PATTERN: awk的输出

                  print item1, item2, ........

 要点:
 (1) 各项目之间使用逗号分隔,而输出时则使用输出分隔符分隔;
 (2) 输出的各item可以字符串或数值、当前记录的字段、变量或awk的表达式;数值会被隐式转换为字符串后输出;
 (3) print后面item如果省略,相当于print $0输出整串内容;要想后面输出空白,使用pirnt " " (在awk中输出子串要使用双引号);


遇到awk的错误需要注意的几点

(1)确保整个a w k命令用单引号括起来。
(2)确保命令内所有引号成对出现。
(3)确保用花括号括起动作语句,用圆括号括起条件语句。
(4)可能忘记使用花括号,也许你认为没有必要,但a w k不这样认为,将按之解释语法




awk的变量:内置变量,自定义变量

   内置变量:输入时的字段分隔符 (awk的内置变量前要用BEGIN)

awk内置变量
A R G C 命令行参数个数
A R G V 命令行参数排列
E N V I R O N 支持队列中系统环境变量的使用
FILENAME a w k浏览的文件名
F N R 浏览文件的记录数
F S 设置输入域分隔符,等价于命令行- F选项
N F 浏览记录的域个数
N R 已读的记录数
O F S 输出域分隔符
O R S 输出记录分隔符
R S 控制记录分隔符
   FS:Field Seperator,  输入时的字段分隔符相当于-F
#  awk 'BEGIN{FS=":"}{print $1,$7}' /etc/passwd  #内置变量的引用要用到双引号



   OFS: Output Field Seperator, 输出时的字段分隔符
#  awk 'BEGIN{FS=":";OFS=":"}{print $1,$7}' /etc/passwd     #语句与语句之间要用分号分割 (红色部分)


   RS:Record Seperator, 输入行分隔符
#  awk 'BEGIN{RS=":"}{print }' /etc/passwd   #会把冒号分开的内容当作每一行来处理

Linux之awk_awk_02

   

  
ORS: Outpput Row Seperator, 输出时的行分隔符在一行内容中
awk 'BEGIN{FS=":";ORS=":"}{print $1,$7}' /etc/passwd


         

Linux之awk_linux_03

 

   

NF:Numbers of Field,字段数    #取最后一个字段还可用$NF
         
NR:Numbers of Record, 行数如果后面跟有多个文件的话那么就是所有文件的一并计数;
# awk 'BEGIN{FS=":";OFS=":"}{print NR $1,$7}' /etc/passwd /etc/group


Linux之awk_linux_04

FNR:行数;各文件分别计数;
# awk 'BEGIN{FS=":";OFS=":"}{print FNR $1,$7}' /etc/passwd

Linux之awk_linux_05

     

ARGV:数组,保存命令本身这个字符,awk '{print $0}' 1.txt 2.txt,意味着ARGV[0]保存awk,

  # awk 'BEGIN{print ARGV[0],ARGV[1],ARGV[2],ARGC}' /etc/passwd /etc/group

Linux之awk_linux_06

  ARGC: 保存awk命令中参数的个数

  自定义变量:输入时的字段分隔符 (awk的内置变量前要用BEGIN

  -v var_name=VALUE   变量名区分字符大小写

  (1) 可以program中定义变量;

  (2) 可以命令行中通过-v选项自定义变量;


# awk 'BEGIN{a="hello awk"}{print a}' /etc/passwd


Linux之awk_linux_07


#awk 'BEGIN{a="hello awk";print a}'      #可以不跟文件  直接输出hello awk
#awk -v a="hello awk" 'BEGIN{print a}'    #直接输出hello awk

awk的printf命令


命令的使用格式:printf format, item1, item2,...  #print 后跟的直接打印内容要加上双引号

要点:

  (1) 要指定format;

  (2) 不会自动换行;如需换行则需要给出\n

  (3) format用于为后面的每个item指定其输出格式;

  (4) 为加入t a b键,使用t a b键速记引用符\ t


format格式的指示符都%开头,后跟一个字符:

   %c: 显示字符的ASCII码;

   %d, %i: 十进制整数;

   %e, %E: 科学计数法显示数值;

   %f: 显示浮点数;

   %g, %G: 以科学计数法格式或浮点数格式显示数值;

   %s: 显示字符串

   %u: 显示无符号整数;

   %%: 显示%自身;

 修饰符:

   #:显示宽度

   -:左对齐

   +:显示数值的符号

   .#: 取值精度


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

Linux之awk_awk_08

 

   
# awk 'BEGIN {printf "%f\n",3.1415}'         #显示7个字节


Linux之awk_linux_09


# awk 'BEGIN {printf "%f\n",3.1415926}'     #总位数超过8位,小数点后面要进行四舍五入


Linux之awk_awk_10

   

# awk 'BEGIN {printf "%15.2f\n",3.1415926}'    #15.2 表示整体为15个位宽加小数点后两位


Linux之awk_linux_11


# awk 'BEGIN {printf "name betle\n-----------"}{print $1"\t",$4}' /etc/passwd


awk输出重定向

  print items > output-file

  print items >> output-file

  print items | command

  特殊文件描述符:

  /dev/stdin: 标准输入

  /dev/stdout: 标准输出

  /dev/stderr: 错误输出

awk的操作符

          

算术操作符:
   x+y
   x-y
   x*y
   x/y
   x**y, x^y
   x%y
   -x:负值
   +x:转换为数值
  字符串操作符:连接
  赋值操作符:
   =
   +=
   -=
   *=
   /=
   %=
   ^=
   **=
   ++
   --
   如果模式自身是=号,要写为/=/
  比较操作符:
   <
   <=
   >
   >=
   ==
   !=
   ~:模式匹配,左边的字符串能够被右边的模式所匹配为真,否则为假;
   !~:
逻辑操作符:
   &&: 与
   ||:或
  条件表达式:
  selector?if-true-expression:if-false-expression
  # awk -F: '{$3>=500?utype="common user":utype="admin or system user";print $1,"is",utype}' /etc/passwd
  函数调用:
  function_name(argu1,argu2)


                

正则表达式的使用

  模式匹配

   为使一域号匹配正则表达式,使用符号‘~’后紧跟正则表达式,也可以用i f语句。a w k中i f后面的条件用()括起来。

cat test.txt

M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansl 05/99   4712 Brown-2 12 30 28

awk '{if($4~/Brown/) print $0}' test.txt

 J.Troll 07/99 4842 Brown-3 12 26 26
 L.Tansl 05/99   4712 Brown-2 12 30 28
 
awk '$0 ~ /Brown/' test.txt   此方法也可以得到同样的结果

 J.Troll 07/99 4842 Brown-3 12 26 26
 L.Tansl 05/99   4712 Brown-2 12 30 28

 精确匹配

  假定要使字符串精确匹配,比如说查看学生序号4 8,文件中有许多学生序号包含4 8,如果在f i e l d - 3中模式查询序号4 8,a w k将返回所有序号带4 8的记录:

awk '{if($3~/48/) print $0}' test.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26

awk '{if ($3== "48") print $0}' test.txt  #准确匹配的内容要用双引号 

P.Bunny 02/99 48 Yellow 12 35 28

awk '{if ($3="48" print $0)}' test.txt
M.Tans 5/99 48 Green 8 40 44
J.Lulu 06/99 48 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 48 Brown-3 12 26 26
L.Tansl 05/99 48 Brown-2 12 30 28

从上面的两个例子可以看出 “=”和“==”是完全不同的一个是复制,一个是等于

不匹配

 假定$4不匹配Brown 都输出

awk '$4 !~ /Brown/' test.txt
M.Tans 5/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28


设置输入域到域变量名

    在a w k中,设置有意义的域名是一种好习惯,在进行模式匹配或关系操作时更容易理解。一般的变量名设置方式为n a m e = $ n,这里n a m e为调用的域变量名, n为实际域号。例如设置学生域名为n a m e,级别域名为b e l t,操作为n a m e = $ 1 ; b e l t s = $ 4。注意分号的使用,它分隔a w k命令。下面例子中,重新赋值学生名域为n a m e,级别域为b e l t s。查询级别为Ye l l o w的记录,并最终打印名称和级别。

awk '{name=$1;belts=$4;if(belts ~/Yellow/) print name" is belt "belts}' grade.txt

P.Bunny is belt Yellow


域值比较


有两种方式测试一数值域是否小于另一数值域。

1) 在B E G I N中给变量名赋值。
2) 在关系操作中使用实际数值。
通常在B E G I N部分赋值是很有益的,可以在a w k表达式进行改动时减少很多麻烦。
使用关系操作必须用圆括号括起来。
下面的例子查询所有比赛中得分在2 7点以下的学生。
用引号将数字引用起来是可选的,“2 7”、2 7产生同样的结果。

awk 'BEGIN{BASELINE="27"} {if ($6<BASELINE) print $0}' grade.txt
J.Lulu 06/99 48317 green 9 24 26
J.Troll 07/99 4842 Brown-3 12 26 26


修改数值域或者取值

  当在a w k中修改任何域时,重要的一点是要记住实际输入文件是不可修改的,修改的只是保存在缓存里的a w k复本。a w k会在变量N R或N F变量中反映出修改痕迹。


  为修改数值域,简单的给域标识重赋新值,如: $ 1 = $ 1 + 5,会将域1数值加5,但要确保赋值域其子集为数值型。


  修改M . Ta n s l e y的目前级别分域,使其数值从4 0减为3 9,使用赋值语句$ 6 = $ 6 - 1,当然在实施修改前首先要匹配域名。

awk '{if($1=="M.Tans") {$6=$6-1};print $1,$6,$7}' grade.txt

M.Tans 39 44
J.Lulu 24 26
P.Bunny 35 28
J.Troll 26 26
L.Tansl 30 28