zoukankan      html  css  js  c++  java
  • Linux九阴真经之无影剑残卷14(文本三剑客之awk)

    awk

    awk是一个报告生成器,它拥有强大的文本格式化能力,这是专业术语。

    你可能不理解所谓的所谓的报告生成器中的“报告”是什么?你可以吧“报告”理解为报表或者表格,也就是说,我们可以利用awk命令,将一些文本整理成我们想要的样子,比如把一些文本整理成表的样子,然后再展示出来,刚才概念中提到的“文本格式化的能力”,也就是这个意思。如果还不理解,不用着急,当你看到后面的示例时,自然会明白awk所擅长的“文本格式化”能力是什么。

    awk早期是在unix上实现的,所以我们现在在linux的所使用的awk其实是gawk,也就是GUN awk ,简称为gawk;awk还有一个版本,New awk , 简称为nawk, 但是linux中最常用的还是gawk。

    grep、 sed 、awk  被称为linux中的“三剑客”

    我们总结一下这三个“剑客”的特长

    grep 更适合单纯的查找或匹配文本

    sed 更适合编辑匹配到的文本

    awk 更适合格式化文本,对文本进行较复杂格式处理

    awk基础

    awk基本语法如下,看不懂没关系,我们会慢慢举例。

    awk [options] 'program' file1,file2,'''

    对于上述语法中的program来说,又可细分为pattern和action,也就是说,awk的基本语法如下

    awk [options] 'Pattern{Action}' file

    从字面意思上理解,action指的就是动作,awk擅长文本格式化,并且将格式化以后的文本输出,所以awk最常用的动作就是print和printf,因为awk要把格式化完成后的文本输出啊,所以这2个动作最常用。

    我们先从最简单的用法开始了解awk,先不适用[optioins],也不指定pattern, 直接适用最简单的action,从而开始认识awk,示例如下

    上图中,我们只是适用awk执行了一个打印的动作,将aaa文件中的内容打印出来

    现在 我们来操作另一个类似场景

    上图中的awk '{print $5}' 表示输出df的信息的第5列,$5表示将当前行按照分隔符分割后的第5列,不指定分隔符时,默认适用空格作为分隔符,信心的你一定发现了,上图用的空格不止有一个,而是有连续多个空格,awk自动将连续的空格理解为一个分隔符了,是不是比cut命令要简单的多?

     awk是逐行处理的,逐行处理的意思就是说,当awk处理一个文本是时,会一行一行进行处理,处理完当前行,再处理下一行,awk默认以“换行符”为标记,识别每一行,也就是说awk根我们人类一样,每次遇到“回车换行”,就人为是当前行的结束,新的一行开始,awk会按照用户指定的分隔符取分割当前行,如果没有指定分隔符,默认使用空格作为分隔符。

    $0表示整行,$NF表示当前行分割后的最后一列($0和$NF均为内置变量)

    注意,$NF和NF 的意思是不一样的,对于awk来说,$NF表示最后一个字段,NF表示当前行被分隔符切开以后,一共有几个字段。

    也就是说,假如一行文本被空格分成了7段,那么NF的值就是7,$NF的值就是$7,而$7表示当前行的第7个字段,也就是最后一列,那么每行的倒数第二列可以写为$(NF-1)

    我们也可以一次输出多列,使用逗号隔开要输出的多个列,如下,一次性输出第一列和第二列

    [root@laobai ~#awk '{print $1,$2}' test
    abc 123
    8ua 456

    同理,也可以一次性输出多个指定的列,如下图

    [root@laobai ~#awk '{print $1,$5}' test
    abc 
    8ua 7y7


    我们发现,第一行没有第五列,所以并没有输出第五列的文本,而第二行有第五列,所以有输出

    除了输出文本中的列,我们还能添加自己的字段,将 自己的字段与文件中的列结合

    从上述实现中可以看出,awk 可以灵活的将我们指定的字符与每一列进行拼接,或者把指定的字符当做一个新列插入到原来的列中,也就是awk格式化文本能力的体现。

    但是要注意,$1 这种内置变量的外侧不能加入双引号,否则$1会被 当做文本输出,如下图

    我们也可以整行输出,如下两种发方法都表示输出整行

    awk包含两种特殊模式:BEGIN和END

    BEGIN模式指定了处理文本之前需要执行的操作

    END模式指定了处理完所有行之后所需要执行的操作

    BEGIN示例

    如果我们想要awk先执行BEGIN模式指定的动作,再根据我们自定义的动作去操作文本,方法如下

    聪明的你一定明白了,END模式就是在处理完所有的指定文本之后,需要指定的动作;那么我们可以结合BEGIN模式和END模式一起使用,示例

    awk分隔符

    (1) 逗号分隔符
    (2) 输出的各item可以字符串,也可以是数值;当前记录的字段、变量或awk的表达式
    (3) 如省略item,相当于print $0

    选项:
    -F 指明输入时用到的字段分隔符
    -v var=value: 自定义变量

    [root@laobai ~#awk -F:  '{print $1"	"$2}' /etc/passwd
    root    x
    bin    x
    daemon    x

    -F:  =      -v FS="="

    [root@laobai ~#awk -v FS=":" '{print $1,$3}' /etc/passwd
    root 0
    bin 1
    daemon 2

    这2个命令时相同效果

    示例

    awk '{print "hello,awk"}'
    awk –F: '{print}' /etc/passwd
    awk –F: ‘{print “wang”}’ /etc/passwd
    awk –F: ‘{print $1}’ /etc/passwd
    awk –F: ‘{print $0}’ /etc/passwd
    awk –F: ‘{print $1”	”$3}’ /etc/passwd
    tail –3 /etc/fstab |awk ‘{print $2,$4}’

    awk变量

    变量:内置变量和自定义变量

    FS:输入字段分隔符,默认为空白字符

    awk -v FS=':' '{print $1,FS,$3}’ /etc/passwd
    awk –F: '{print $1,$3,$7}’ /etc/passwd

    OFS:输出字段分隔符,默认为空白字符

    awk -v FS=":" -vOFS=":"  '{print $1,$3,$7}'  /etc/passwd

    RS:  输入记录分隔符,指定输入时的换行符

    awk -v RS=" "  '{print}'  /etc/passwd

    ORS:输出记录分隔符,输出时用指定符号代替换行符

    awk -v RS=' ' -v ORS='###'‘{print }’ /etc/passwd

    NF:字段数量(一行有多少列)

    awk -F: ‘{print NF}’ /etc/fstab,引用内置变量不用$
    awk -F: '{print $(NF-1)}' /etc/passwd

    NR:记录号(也就是行号)

    awk '{print NR}' /etc/fstab ; awk END'{print NR}' /etc/fstab

    FNR:  各文件分别计数,记录号

    awk '{print FNR}' /etc/fstab /etc/inittab

    FILENAME: 当前文件名

    awk '{print FILENAME}’ /etc/fstab

    ARGC:命令行参数个数

    awk '{print ARGC}’ /etc/fstab /etc/inittab
    awk ‘BEGIN {print ARGC}’ /etc/fstab /etc/inittab

    ARGV:数组,保存的是命令行所给定的各参数

    awk ‘BEGIN {print ARGV[0]}’ /etc/fstab /etc/inittab
    awk ‘BEGIN {print ARGV[1]}’ /etc/fstab /etc/inittab

    注意:在awk中,只有在引用$0,$1,$2等内置变量才会用到$符号,当在引用其他变量时,不管是内置变量还是自定义变量,都不适用$符,而是直接适用变量名

    [root@laobai ~#df | awk '{print NR,$1}'
    1 Filesystem
    2 /dev/sda2
    3 /dev/sda1
    4 /dev/sda


    自定义变量

    自定义变量(区分字符大小写)

           (1)-v  var-value

           (2)在program中 直接定义

    示例

    [root@laobai ~#awk -v var=myvar 'BEGIN{print var}'
    myvar
    [root@laobai ~#awk 'BEGIN{myvar="ttt";print myvar}'
    ttt

    也可以一次性定义多个变量

    [root@laobai ~#awk 'BEGIN{myvar="111";yourvar="222";print myvar,yourvar}'
    111 222

     
    也可以在awk中引用shell 变量

    [root@laobai ~#var=666
    [root@laobai ~#awk -v myvar=$var 'BEGIN{print myvar}'
    666

    示例:

    awk -v test='hello gawk' '{print test}' /etc/fstab
    awk -v test='hello gawk' 'BEGIN{print test}'
    awk 'BEGIN{test="hello,gawk";print test}'
    awk -F: '{sex="male";print $1,sex,age;age=18}' /etc/passwd
    cat awkscript
    {print script,$1,$2}
    awk -F: -f awkscript script=“awk” /etc/passwd


    printf命令

    print  "FORMAT"  ,item1,item2 ....     格式化输出命令

    (1)必须指定FORMAT

    (2)不会自动换行,需要显示给出换行控制符,

    (3)FORMAT中需要分别为后面每个item指定格式符

    格式符:与item一一对应


    %c: 显示字符的ASCII码


    %d, %i: 显示十进制整数


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


    %f:显示为浮点数


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


    %s:显示字符串


    %u:无符号整数


    %%: 显示%自身


    修饰符:
    #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
    -: 左对齐(默认右对齐) %-15s
    +:显示数值的正负符号 %+d

    示例

    awk -F: ‘{printf "%s",$1}’ /etc/passwd
    awk -F: ‘{printf "%s
    ",$1}’ /etc/passwd
    awk -F: '{printf "%-20s %10d
    ",$1,$3}' /etc/passwd
    awk -F: ‘{printf "Username: %s
    ",$1}’ /etc/passwd
    awk -F: ‘{printf “Username: %s,UID:%d
    ",$1,$3}’ /etc/passwd
    awk -F: ‘{printf "Username: %15s,UID:%d
    ",$1,$3}’ /etc/passwd
    awk -F: ‘{printf "Username: %-15s,UID:%d
    ",$1,$3}’ /etc/passwd


    操作符

    +   -   *   /      ^     %(取模,也就是除 留下的余数),例如3取10的模 就是 1

    -x     整数转换为负数

    +x    转换为数值

    赋值操作符

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

    比较操作符:

    ==,!=,>, >=, <,  <=

    模式匹配符

    ~:    左边的字符串是否能和右边的模式匹配

    !~:左边的字符串是否能和右边的模式不匹配

    示例

    awk –F: '$0 ~ /root/{print $1}‘ /etc/passwd
    awk '$0~“^root"' /etc/passwd
    awk '$0 !~ /root/‘ /etc/passwd
    awk –F: ‘$3==0’ /etc/passwd


    逻辑操作符

    逻辑与&&, 逻辑或|| ,非 !

    示例

    awk –F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd
    awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd
    awk -F: ‘!($3==0) {print $1}' /etc/passwd
    awk -F: ‘!($3>=500) {print $3}’ /etc/passwd
    函数调用: function_name(argu1, argu2, ...)

    条件表达式(三目表达式):


    selector?if-true-expression:if-false-expression


    示例:

    awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s
    ",$1,usertype}' /etc/passwd

    awk PATTERN

    PATTERN:根据pattern条件,过滤匹配的行,再做处理

    (1):空模式,匹配每一行

    (2):/ 正则表达式/,仅能处理正则表达式匹配到的行,需要用/ /括起来

    (3):关系表达式,结果为“真”(非0或非空字符串),才会被处理;“假”(0或空字符串)则不处理

    (4):地址定界,支持PATTERN和条件表达式;不支持数字直接定界

    line ranges:行范围(从A行到B行之间的内容)

    示例

    awk -F: ‘/^root>/,/^nobody>/{print $1}' /etc/passwd
    awk -F: ‘(NR>=10&&NR<=20){print NR,$1}' /etc/passwd

    (5):BEGIN模式,BEGIN{}:仅在文本处理之前执行;END{}:仅在文本处理完成之后执行

    示例

    awk -F: 'i=1;j=1{print i,j}' /etc/passwd
    awk –F: '$3>=1000{print $1,$3}' /etc/passwd
    awk -F: '$3<1000{print $1,$3}' /etc/passwd
    awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
    awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd


     

    [root@laobai ~#awk -F: 'BEGIN {print "USER USERID"}{print $1":"$3} END{print "end file"}' /etc/passwd
    [root@laobai ~#awk -F : '{print "USER USERID";print $1":"$3} END{print "end file"}' /etc/passwd
    [root@laobai ~#awk -F: 'BEGIN{print " USER UID 
    --------------- "}{print $1,$3}' /etc/passwd

    seq 10 |awk ‘i=0’
    seq 10 |awk ‘i=1’
    seq 10 | awk 'i=!i‘
    seq 10 | awk '{i=!i;print i}‘
    seq 10 | awk '!(i=!i)'
    seq 10 |awk -v i=1 'i=!i'

    将passwd里 UUID大于500的值取出来,并以 USER   USERID开头  且对齐

    控制语句

    (1)if-else 语法:if(condition) statement [else statement]      

    awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
    
    awk -F: '{if($3>=1000) {printf "Common user: %s
    ",$1} else {printf "root or Sysuser: %s
    ",$1}}' /etc/passwd


    (2)while 语法:while(condition) statement 条件真进入循环,条件假退出循环

    awk ‘/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=10) {print $i,length($i)}; i++}}’ /etc/grub2.cfg

    (3)do-while 语法:do statement while(condittion) 先执行一次do,然后判断是否进入循环

    [root@laobai ~#awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print total}'
    
    5050

    (4)for 语法:for(variable assignment; condition; iteration process){for-body}    *特殊用法:能够遍历数组中的元素,for(var in array) {for-body}

    awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg


    (5)switch 语法:switch(expression) {case VALUE1 or /PEGEXP/: statement; case VALUE2 or /PEGEXP2/: statement; ... ;default: statement}

     switch 后面跟的变量 被 后面的case 匹配到   则执行

    (6)next:控制awk的内生循环,提前结束对本行的处理直接进入下一行

    awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd


    (7)continue | break | exit:退出循环,和shell中用法相同

    continue 是结束当前循环,下一个循环继续运行

    break 是结束循环

    awk数组

    关联数组:array [index-expression]

    index - expression:

    (1)可使用任意字符串;字符串要使用双引号括起来

    (2)如果 某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”

    (3)若要判断数组中是否存在某元素,要使用“index in array” 格式进行遍历

     

     

     

     

  • 相关阅读:
    hdu-2612-Find a way
    poj-1426-Find The Multiple
    POJ-2251-Dungeon Master
    树的遍历
    前序和中序+后序和中序
    哈夫曼树
    平衡二叉树
    队列和优先队列
    1213
    1163
  • 原文地址:https://www.cnblogs.com/huxiaojun/p/9048577.html
Copyright © 2011-2022 走看看