zoukankan      html  css  js  c++  java
  • "三剑客"之awk心中无剑

    一、awk介绍

      awk 是一种程序语言. 它具有一般程序语言常见的功能.
      因awk语言具有某些特点, 如 : 使用直译器(Interpreter)不需先行编译; 变量无类型之分(Typeless), 可使用文字当数组的下标(Associative Array)...等特色. 因此, 使用awk撰写程序比起使用其它语言更简洁便利且节省时间. awk还具有一些内建功能, 使得awk擅于处理具数据行(Record), 字段(Field)型态的资料; 此外, awk内建有pipe的功能, 可将处理中的数据传送给外部的 Shell命令加以处理, 再将Shell命令处理后的数据传回awk程序, 这个特点也使得awk程序很容易使用系统资源。

    1.1、awk读取数据设置

    awk读取输入文件时,每次读取一条记录(record)(默认情况下按行读取,所以此时记录就是行)。每读取一条记录,将其保存到$0中,然后执行一次main代码段
    在读取数据时,可设置表示输入记录分隔符的预定义变量RS(Record Separator)来改变每次读取的记录模式

    awk 'BEGIN{RS="
    "}'默认情况下使用
    换行符进行分隔读取。
    RS="":按段落读取
    RS="":一次性读取所有数据,但有些特殊文件中包含了空字符0
    RS="^$":真正的一次性读取所有数据,因为非空文件不可能匹配成功
    RS="
    +":按行读取,但忽略所有空行

    所以在读取数据前,可以改变RS来改变每次读取数据的模式

    1.2、awk的执行过程

    1. 自动从指定的数据文件中读取一个数据行.
    2. 自动更新(Update)相关的内建变量之值. 如 : NF, NR, $0...
    3. 依次执行程序中 所有 的 Pattern { Actions } 指令.
    4. 当执行完程序中所有 Pattern { Actions } 时, 若数据文件中还有未读取的数据, 则反复执行步骤1到步骤4.

    1.3、本文实例数据

    ID      name    gender  age     email           phone
    1       zhangs  female  34      aaa@163.com     13423394013
    2       lisi    male    45      abb@gmail.com   15608492523
    3       wmzi    female  61      ccc@sohu.com    18848796505
    4       wangw   female  31      acc@sohu.com    18848796506
    5       liub    male    56      gdd@163.com     18848796507
    6       zhangf  female  34      adb@139.com     18848796573
    7       guany   male    56      eab@189.com     17048796508
    8       zhaoy   female  35      189@163.com     17058796503
    9       huangz  male    65      dd@189.com      13648796593
    10      lvbu    male    43      byy@sohu.com    18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803

    二、awk使用语法和选项

    2.1、使用语法

    awk [ -- ] program-text file ...        (1)
    awk -f program-file [ -- ] file ...     (2)
    awk -e program-text [ -- ] file ...     (3)
    
    program-txt是awk命令行中的awk代码,一般使用单引号包围
    -f program-file表示将awk代码写在文件中,然后使用-f选项去执行该文件
    -e program-text也用于指定awk代码,如果要结合文件和命令行一起使用,必须使用-e和-f,不能使用(1)

    2.2、使用选项

    -e program-text
    --source program-text
    指定awk程序表达式,可结合-f选项同时使用
    在使用了-f选项后,如果不使用-e,awk program是不会执行的,它会被当作ARGV的一个参数
    
    -f program-file
    --file program-file
    从文件中读取awk源代码来执行,可指定多个-f选项
    
    -F fs
    --field-separator fs
    指定输入字段分隔符(FS预定义变量也可设置)
    
    -n
    --non-decimal-data
    识别文件输入中的8进制数(0开头)和16进制数(0x开头)
    echo '030' | awk -n '{print $1+0}'
    
    -o [filename]
    格式化awk代码。
    不指定filename时,则默认保存到awkprof.out
    指定为`-`时,表示输出到标准输出
    
    -v var=val
    --assign var=val
    在BEGIN之前,声明并赋值变量var,变量可在BEGIN中使用

    2.3、awk程序语法

    awk程序中主要语法是 Pattern { Actions}
    Pattern:为匹配模式,即判定某一行是否符合该模式,然后才执行Actions
    Actions:为执行逻辑,其中多个action如果写在同一行,则需使用分号分隔


    pattern和action都可以省略

    • 省略pattern,等价于对每一行数据都执行action
    • 例如:awk '{print $0}' test.txt

    省略代码块{action},等价于{print}即输出所有行

    • 例如:awk '/Alice/' test.txt等价于awk '/Alice/{print $0}' test.txt
    • 省略代码块中的action,表示对筛选的行什么都不做
    • 例如:awk '/Alice/{}' test.txt

    pattern{action}任何一部分都可以省略

    • 例如:awk '' test.txt

    pattern中使用逻辑与、非、或

    pattern && pattern # 逻辑与 3>2 && 3>1 {action}
    pattern || pattern # 逻辑或 3>2 || 3<1 {action}
    ! pattern # 逻辑取反 !/a.*ef/{action}

    常用的Pattern

    # 1.根据行号筛选
    NR==2   # 筛选出第二行
    NR>=2   # 输出第2行和之后的行
    
    # 2.根据正则表达式筛选整行
    /qq.com/       # 输出带有qq.com的行
    $0 ~ /qq.com/  # 等价于上面命令
    /^[^@]+$/      # 输出不包含@符号的行
    !/@/           # 输出不包含@符号的行
    
    # 3.根据字段来筛选行
    ($4+0) > 24{print $0}  # 输出第4字段大于24的行
    $5 ~ /qq.com/          # 输出第5字段包含qq.com的行
    
    # 4.将多个筛选条件结合起来进行筛选
    NR>=2 && NR<=7             #输出行数大于等于2并且小于等于7
    $3=="male" && $6 ~ /^170/  #输出第三个字段等于male并且第六个字段不用170开头    
    $3=="male" || $6 ~ /^170/  #输出第三个字段等于male或者第六个字段不用170开头
    
    # 5.按照范围进行筛选 flip flop
    # pattern1,pattern2{action}
    NR==2,NR==7         # 输出第2到第7行
    NR==2,$6 ~ /^170/   # 输出第2行到匹配第六个字段170开头的行

    action有很多,例如:

    awk的 I/O指令 : print, printf( ), getline...
    awk的 流程控制指令 : if(...){..} else{..}, while(...){...}...

    实例:

    [root@lgh test]# awk '' test.txt #action和pattern都省了
    [root@lgh test]# awk '/163/{print $0}' test.txt  #匹配行中有163的行,并输出
    1       zhangs  female  34      aaa@163.com     13423394013
    5       liub    male    56      gdd@163.com     18848796507
    8       zhaoy   female  35      189@163.com     17058796503
    [root@lgh test]# awk '/163/' test.txt  #省略action,默认输出$0
    1       zhangs  female  34      aaa@163.com     13423394013
    5       liub    male    56      gdd@163.com     18848796507
    8       zhaoy   female  35      189@163.com     17058796503
    [root@lgh test]# awk '{print $0}' test.txt #省略pattern,输出全部行
    ID      name    gender  age     email           phone
    1       zhangs  female  34      aaa@163.com     13423394013
    2       lisi    male    45      abb@gmail.com   15608492523
    3       wmzi    female  61      ccc@sohu.com    18848796505
    4       wangw   female  31      acc@sohu.com    18848796506
    5       liub    male    56      gdd@163.com     18848796507
    6       zhangf  female  34      adb@139.com     18848796573
    7       guany   male    56      eab@189.com     17048796508
    8       zhaoy   female  35      189@163.com     17058796503
    9       huangz  male    65      dd@189.com      13648796593
    10      lvbu    male    43      byy@sohu.com    18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803

    三、awk内置变量

    预定义变量分为两类:控制awk工作的变量和携带信息的变量

    第一类:控制AWK工作的预定义变量

    $0,$1,$2,...$NF:其中$0表示整行数据,$1,$2,...,$NF分别表示该行的第一个字段、第二个字段,...,最后一个字段
    RS:输入记录分隔符,默认为换行符 IGNORECASE:默认值为0,表示所有的正则匹配不忽略大小写。设置为非0值(例如1),之后的匹配将忽略大小写。例如在BEGIN块中将其设置为1,将使FS、RS都以忽略大小写的方式分隔字段或分隔record FS:读取记录后,划分为字段的字段分隔符。 FIELDWIDTHS:以指定宽度切割字段而非按照FS。 FPAT:以正则匹配匹配到的结果作为字段,而非按照FS划分。 OFS:print命令输出各字段列表时的输出字段分隔符,默认为空格" " ORS:print命令输出数据时在尾部自动添加的记录分隔符,默认为换行符 CONVFMT:在awk中数值隐式转换为字符串时,将根据CONVFMT的格式按照sprintf()的方式自动转换为字符串。默认值为”
    %.6g OFMT:在print中,数值会根据OFMT的格式按照sprintf()的方式自动转换为字符串。默认值为”%.6g

    第二类:携带信息的预定义变量

    ARGC和ARGV:awk命令行参数的数量、命令参数的数组。
    ARGIND:awk当前正在处理的文件在ARGV中的索引位置。所以,如果awk正在处理命令行参数中的某文件,则ARGV[ARGIND] == FILENAME为真
    FILENAME:awk当前正在处理的文件(命令行中指定的文件),所以在BEGIN中该变量值为空
    ENVIRON:保存了Shell的环境变量的数组。例如ENVIRON["HOME"]将返回当前用户的家目录
    NR:当前已读总记录数,多个文件从不会重置为0,所以它是一直叠加的,可以直接修改NR,下次读取记录时将在此修改值上自增
    FNR:当前正在读取文件的第几条记录,每次打开新文件会重置为0,可以直接修改FNR,下次读取记录时将在此修改值上自增
    NF:当前记录的字段数,
    RT:在读取记录时真正的记录分隔符,
    RLENGTH:match()函数正则匹配成功时,所匹配到的字符串长度,如果匹配失败,该变量值为-1
    RSTART:match()函数匹配成功时,其首字符的索引位置,如果匹配失败,该变量值为0
    SUBSEP:arr[x,y]中下标分隔符构建成索引时对应的字符,默认值为034,是一个不太可能出现在字符串中的不可打印字符。

    1、$0,$1,$2,...,$NF

    [root@lgh test]# awk '{print $0,$1,$2,$NF}' test.txt #输出整行,然后输出第一,第二,和最后一个字段的值
    ID      name    gender  age     email           phone ID name phone
    1       zhangs  female  34      aaa@163.com     13423394013 1 zhangs 13423394013
    2       lisi    male    45      abb@gmail.com   15608492523 2 lisi 15608492523
    3       wmzi    female  61      ccc@sohu.com    18848796505 3 wmzi 18848796505
    4       wangw   female  31      acc@sohu.com    18848796506 4 wangw 18848796506
    5       liub    male    56      gdd@163.com     18848796507 5 liub 18848796507
    6       zhangf  female  34      adb@139.com     18848796573 6 zhangf 18848796573
    7       guany   male    56      eab@189.com     17048796508 7 guany 17048796508
    8       zhaoy   female  35      189@163.com     17058796503 8 zhaoy 17058796503
    9       huangz  male    65      dd@189.com      13648796593 9 huangz 13648796593
    10      lvbu    male    43      byy@sohu.com    18148796803 10 lvbu 18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803 11 sunwk 18148706803

    2、RS、IGNORECASE

    RS有两种情况:
    RS为单个字符:直接使用该字符作为分隔符进行分割记录
    RS为多个字符:将其当做正则表达式,只要匹配正则表达式的符号,都用来做分割记录,设置预定义变量INGORECASE为非零值,表示正则忽略大小写

    [root@lgh test]# echo "bababaABAB" |  awk 'BEGIN{IGNORECASE=1;RS="a+"}{print $0}' #正则忽略大小写切分读取
    b
    b
    b
    B
    B
    [root@lgh test]# echo "bababaABAB" |  awk 'BEGIN{IGNORECASE=0;RS="a+"}{print $0}' #正则不忽略大小写切分读取
    b
    b
    b
    ABAB
    [root@lgh test]# echo "bababaABAB" |  awk 'BEGIN{IGNORECASE=1;RS="a"}{print $0}' #非正则,IGNORECASE无效
    b
    b
    b
    ABAB

    3、分隔符FS、FIELDWIDTHS

    • FS为单个字符时,该字符即为字段分隔符
    • FS为多个字符时,则采用正则表达式模式作为字段分隔符
    • 特殊的,也是FS默认的情况,FS为单个空格时,将以连续的空白(空格、制表符、换行符)作为字段分隔符
    • 特殊的,FS为空字符串””时,将对每个字符都进行分隔,即每个字符都作为一个字段
    • 设置预定义变量IGNORECASE为非零值,正则匹配时表示忽略大小写(只影响正则,所以FS为单字时无影响)
    [root@lgh test]# awk -F" +|@" '{print $1,$2,$3,$4,$5,$6,$7}' test.txt #正则,一个空格或者多个空格,或者@符进行分割
    ID name gender age email phone
    1 zhangs female 34 aaa 163.com 13423394013
    2 lisi male 45 abb gmail.com 15608492523
    3 wmzi female 61 ccc sohu.com 18848796505
    4 wangw female 31 acc sohu.com 18848796506
    5 liub male 56 gdd 163.com 18848796507
    6 zhangf female 34 adb 139.com 18848796573
    7 guany male 56 eab 189.com 17048796508
    8 zhaoy female 35 189 163.com 17058796503
    9 huangz male 65 dd 189.com 13648796593
    10 lvbu male 43 byy sohu.com 18148796803
    11 sunwk male 99 suk sohu.com 18148706803
    [root@lgh test]#  awk  '{print $1,$2}' FS=":" /etc/passwd | head -2 #使用FS指定:为分割分
    root x
    bin x
    [root@lgh test]# awk -F":" '{print $1,$2}' /etc/passwd | head -2 #使用-F指定:分隔符
    root x
    bin x

    预定义变量FIELDWIDTHS按字符宽度分割字段
    FIELDWIDTHS="1 2 3 4" :表示第一个字段1个字符,第二个2个字符,第三个3个字符,第四个4个字符

    [root@lgh test]# cat name.txt #文件内容
    ID      name    gender  age     email           phone
    1               female  34      aaa@163.com     13423394013
    2       lisi    male    45      abb@gmail.com   15608492523
    [root@lgh test]#  awk '{print $2}' name.txt #获取第二列,其中female是第三列的内容
    name
    female
    lisi
    [root@lgh test]# awk 'BEGIN{FIELDWIDTHS="8 8"}{print $2}' name.txt #正确的获取第二列
    name
    
    lisi
    [root@lgh test]# echo 'bababaABAB' | awk 'BEGIN{FIELDWIDTHS="1 2 3 4"}{print $1,$2,$3,$4}' #使用指定字符长度分割
    b ab aba ABAB

    4、FS、OFS

    FS是读取行时的字段分割,OFS是输出时的字段分隔符

    [root@lgh test]# awk 'BEGIN{OFS="="}{print $1,$2}' test.txt
    ID=name
    1=zhangs
    2=lisi
    3=wmzi
    4=wangw
    5=liub
    6=zhangf
    7=guany
    8=zhaoy
    9=huangz
    10=lvbu
    11=sunwk
    [root@lgh test]# awk 'BEGIN{OFS="-"}{$1=$1;print $0}' test.txt #重组$0,修改$1,$2,...,$NF中的内容,以及增大或者减少NF的值,都会产生$0的重组
    ID-name-gender-age-email-phone
    1-zhangs-female-34-aaa@163.com-13423394013
    2-lisi-male-45-abb@gmail.com-15608492523
    3-wmzi-female-61-ccc@sohu.com-18848796505
    4-wangw-female-31-acc@sohu.com-18848796506
    5-liub-male-56-gdd@163.com-18848796507
    6-zhangf-female-34-adb@139.com-18848796573
    7-guany-male-56-eab@189.com-17048796508
    8-zhaoy-female-35-189@163.com-17058796503
    9-huangz-male-65-dd@189.com-13648796593
    10-lvbu-male-43-byy@sohu.com-18148796803
    11-sunwk-male-99-suk@sohu.com-18148706803

    5、RS、ORS

    RS是读取时的行分割,ORS是输出时的行分割

    [root@lgh test]# echo -e "a
    b" |awk '{print $1}' #RS默认
    
    a
    b
    [root@lgh test]# echo -e "a
    b" |awk 'BEGIN{ORS="hello word"}{print $1}' #使用hello word代替
    进行输出
    ahello wordbhello word

    6、OFMT

    变量OFMT(Output format)定义的格式按照sprintf()相同的方式进行格式化。OFMT默认值为%.6g,表示有效位(整数部分加小数部分)最多为6。

    [root@lgh test]# awk 'BEGIN{OFMT="%.3f";print 3.141592678}' #会进行四舍五入
    3.142
    [root@lgh test]# awk 'BEGIN{print 3.141592678}'
    3.14159
    常用格式符:
    %c :将ASCII码转换为字符,
    %d :转为整数
    %e :科学计数方法输出
    %o :转为8进制输出
    %s :转为字符串
    %x :转16进制

    7、NR、FNR

    NR是该程序读取的记录数,FNR是程序读取某个文件的的记录数

    [root@lgh test]# awk '{print NR,FNR,$0}' test.txt test.txt #NR一直递增,FNR根据文件重新设置1,然后递增
    1 1 ID      name    gender  age     email           phone
    2 2 1       zhangs  female  34      aaa@163.com     13423394013
    3 3 2       lisi    male    45      abb@gmail.com   15608492523
    4 4 3       wmzi    female  61      ccc@sohu.com    18848796505
    5 5 4       wangw   female  31      acc@sohu.com    18848796506
    6 6 5       liub    male    56      gdd@163.com     18848796507
    7 7 6       zhangf  female  34      adb@139.com     18848796573
    8 8 7       guany   male    56      eab@189.com     17048796508
    9 9 8       zhaoy   female  35      189@163.com     17058796503
    10 10 9       huangz  male    65      dd@189.com      13648796593
    11 11 10      lvbu    male    43      byy@sohu.com    18148796803
    12 12 11      sunwk   male    99      suk@sohu.com    18148706803
    13 1 ID      name    gender  age     email           phone
    14 2 1       zhangs  female  34      aaa@163.com     13423394013
    15 3 2       lisi    male    45      abb@gmail.com   15608492523
    16 4 3       wmzi    female  61      ccc@sohu.com    18848796505
    17 5 4       wangw   female  31      acc@sohu.com    18848796506
    18 6 5       liub    male    56      gdd@163.com     18848796507
    19 7 6       zhangf  female  34      adb@139.com     18848796573
    20 8 7       guany   male    56      eab@189.com     17048796508
    21 9 8       zhaoy   female  35      189@163.com     17058796503
    22 10 9       huangz  male    65      dd@189.com      13648796593
    23 11 10      lvbu    male    43      byy@sohu.com    18148796803
    24 12 11      sunwk   male    99      suk@sohu.com    18148706803

    8、ARGS、ARGV

    预定义变量ARGV是一个数组,包含了所有的命令行参数。该数组使用从0开始的数值作为索引。
    预定义变量ARGC初始时是ARGV数组的长度,即命令行参数的数量。
    ARGV数组的数量和ARGC的值只有在awk刚开始运行的时候是保证相等的。

    [root@lgh test]# awk 'BEGIN{print ARGC;for(i in ARGV){print "ARGV[" i "]= " ARGV[i]}}' a b c
    4
    ARGV[0]= awk
    ARGV[1]= a
    ARGV[2]= b
    ARGV[3]= c

    四、BEGIN和END

    awk的所有代码(目前这么认为)都是写在语句块中的。
    每个语句块前面可以有pattern,比如:pattern1{action1}pattern2{action3;action4;...}
    语句块可分为3类:BEGIN语句块、END语句块和main语句块。其中BEGIN语句块和END语句块都是的格式分别为BEGIN{...}和END{...},而main语句块是一种统称,它的pattern部分没有固定格式,也可以省略,main代码块是在读取文件的每一行的时候都执行的代码块。

    BEGIN代码块:
        在读取文件之前执行,且执行一次
        在BEGIN代码块中,无法使用$0或其它一些特殊变量
    
    main代码块:
        读取文件时循环执行,(默认情况)每读取一行,就执行一次main代码块
        main代码块可有多个
    
    END代码块:
        在读取文件完成之后执行,且执行一次
        有END代码块,必有要读取的数据(可以是标准输入)
        END代码块中可以使用$0等一些特殊变量,只不过这些特殊变量保存的是最后一轮awk循环的数据
    [root@lgh test]# awk 'BEGIN{print "hello word"}/^1/{print $0}END{print "END hello word"}' test.txt
    hello word
    1       zhangs  female  34      aaa@163.com     13423394013
    10      lvbu    male    43      byy@sohu.com    18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803
    END hello word

    实例如上:begin和end均只输出一次,main代码中匹配1开头的行并输出

    五、运算符和逻辑运算符

    5.1、运算符

    $      # $(2+2)
    ++ --
    ^ **
    + - !   # 一元运算符
    * / %
    + -
    space  # 这是字符连接操作 `12 " " 23`  `12 " " -23`
    | |&
    < > <= >= != ==   # 注意>即是大于号,也是print/printf的重定向符号
    ~ !~
    in
    &&
    ||
    ?:
    = += -= *= /= %= ^=

    5.2、逻辑运算符

    &&          逻辑与
    ||          逻辑或
    !           逻辑取反
    
    expr1 && expr2  # 如果expr1为假,则不用计算expr2
    expr1 || expr2  # 如果expr1为真,则不用计算expr2
    
    # 注:
    # 1. && ||会短路运算
    # 2. !优先级高于&&和||
    #    所以`! expr1 && expr2`等价于`(! expr1) && expr2`

    六、输出和重定向

    输出格式:print elem1,elem2,elem3...
    逗号分隔要打印的字段列表,各字段都会自动转换成字符串格式,然后通过预定义变量OFS值(其默认值为空格)连接各字段进行输出
    在print输出时会自动在尾部加上输出记录分隔符,输出记录分隔符的预定义变量为ORS,其默认值为
    sprintf()采用和printf相同的方式格式化字符串,但是它不会输出格式化后的字符串,而是返回格式化后的字符串。所以,可以将格式化后的字符串赋值给某个变量。

    [root@lgh test]# awk 'BEGIN{var=sprintf("%.3f",3.141592678);print var}' #使用sprintf进行格式化复制变量,然后输出
    3.142
    [root@lgh test]# awk 'BEGIN{var=sprintf("%d",3.141592678);print var}'
    3
    [root@lgh test]# awk 'BEGIN{OFMT="%.3f";print 3.141592678}' #使用OFMT指定格式进行输出
    3.142
    [root@lgh test]# awk 'BEGIN{OFMT="%d";print 3.141592678}'
    3

     重定向:

    >filename时,如果文件不存在,则创建,如果文件存在则首先截断。之后再输出到该文件时将不再截断。
    >>filename时,将追加数据,文件不存在时则创建。
    awk中只要不close(),任何文件都只会在第一次使用时打开,之后都不会再重新打开。

    [root@lgh test]# awk '{print $1,$2 > "name.txt"}' test.txt #重定向到name.txt文件
    [root@lgh test]# cat name.txt
    ID name
    1 zhangs
    2 lisi
    3 wmzi
    4 wangw
    5 liub
    6 zhangf
    7 guany
    8 zhaoy
    9 huangz
    10 lvbu
    11 sunwk

    七、数组

    awk数组特性:

    • awk的数组是关联数组(即key/value方式的hash数据结构),索引下标可为数值(甚至是负数、小数等),也可为字符串
    • 在内部,awk数组的索引全都是字符串,即使是数值索引在使用时内部也会转换成字符串
    • awk的数组元素的顺序和元素插入时的顺序很可能是不相同的
    • awk数组支持数组的数组
    • 通过索引的方式访问数组中不存在的元素时,会返回空字符串,同时会创建这个元素并将其值设置为空字符串。

    数组的访问和赋值都是通过制定下标就行赋值和访问,下标可以为整数,负数,字符串,小数
    例如:

    [root@lgh test]# cat a.awk
    {
            arr[i]="a"
            arr[-1]="b"
            print arr[i]
            print arr[-1]
            print length(arr)
            print arr[1]  #输出不存在的数组下标内容
            print "################"
            print length(arr)  #长度+1
    }
    [root@lgh test]# echo 0 | awk -f a.awk
    a
    b
    2
    
    ################
    3

    数组遍历、删除、判读某key是否在arr中:

    [root@lgh test]# cat a.awk
    {
            arr["x"]="a"
            arr[-1]="b"
            arr[4.5]=1
            arr[4]=5
            for(idx in arr){ #遍历
               print arr[idx]
            }
            print "###############"
            delete arr[4]  #删除元素,也可以删除整个数组delete arr
            for(idx in arr){ #遍历无序,与建立数组时输入的元素顺序不一定一致
               print idx,arr[idx]
            }
            print "##############"
            print (-1 in arr) #判断key=-1,是否在数组中,是返回1,不是返回0
            print (99 in arr)
    }
    [root@lgh test]# echo 0 | awk -f a.awk
    5
    b
    a
    1
    ###############
    -1 b
    x a
    4.5 1
    ##############
    1
    0

    八、流程控制

    8.1、判断、选择

    if判断:

    if(condition){statement }
    if(condition){statement }else{statement }
    if(condition){statement }else if(condition){statement }else{statement }

    三目运算

    expr1 ? expr2 : expr3

    switch选择:

    switch (expression) {
        case value1|regex1 : statements1
        case value2|regex2 : statements2
        case value3|regex3 : statements3
        ...
        [ default: statement ]
    }
    [root@lgh test]# awk 'NR>1{$1>5? var="B": var="A";print $1,var}' test.txt
    1 A
    2 A
    3 A
    4 A
    5 A
    6 B
    7 B
    8 B
    9 B
    10 B
    11 B
    [root@lgh test]# awk 'NR>1{if($1<5){print $1,"A"}else{print $1,"B"}}' test.txt
    1 A
    2 A
    3 A
    4 A
    5 B
    6 B
    7 B
    8 B
    9 B
    10 B
    11 B

    8.2、循环

    while和do…while

    while(condition){
        statements
    }
    
    do {
        statements
    } while(condition)

    for循环

    for (expr1; expr2; expr3) {
        statement
    }
    
    for (idx in array) {
        statement
    }
    awk '{i=1;while(i<=NF){print $i;i++}}' test.txt 
    awk '{for(i=1;i<=NF;i++) print $i}' test.txt

    8.3、关键字

    break:break可退出for、while、do…while、switch语句。
    continue:continue可让for、while、do…while进入下一轮循环。
    next:next会在当前语句处立即停止后续操作,并读取下一行,进入循环顶部。
    nextfile:nextfile会在当前语句处立即停止后续操作,并直接读取下一个文件,并进入循环顶部。
    exit:直接退出awk程序,注意,END语句块也是exit操作的一部分,所以在BEGIN或main段中执行exit操作,也会执行END语句块。如果真的想直接退出整个awk,则可以先设置一个flag变量,然后在END语句块的开头检查这个变量再exit。

     

    [root@lgh test]# awk 'NR==3{next}{print  $0}' test.txt  #匹配到了第3行,然后跳过
    ID      name    gender  age     email           phone
    1       zhangs  female  34      aaa@163.com     13423394013
    3       wmzi    female  61      ccc@sohu.com    18848796505
    4       wangw   female  31      acc@sohu.com    18848796506
    5       liub    male    56      gdd@163.com     18848796507
    6       zhangf  female  34      adb@139.com     18848796573
    7       guany   male    56      eab@189.com     17048796508
    8       zhaoy   female  35      189@163.com     17058796503
    9       huangz  male    65      dd@189.com      13648796593
    10      lvbu    male    43      byy@sohu.com    18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803
    [root@lgh test]# awk 'NR==3{nextfile}{print  $0}' test.txt test.txt #匹配到第3行,跳过该文件,执行下一个文件
    ID      name    gender  age     email           phone
    1       zhangs  female  34      aaa@163.com     13423394013
    ID      name    gender  age     email           phone
    1       zhangs  female  34      aaa@163.com     13423394013
    2       lisi    male    45      abb@gmail.com   15608492523
    3       wmzi    female  61      ccc@sohu.com    18848796505
    4       wangw   female  31      acc@sohu.com    18848796506
    5       liub    male    56      gdd@163.com     18848796507
    6       zhangf  female  34      adb@139.com     18848796573
    7       guany   male    56      eab@189.com     17048796508
    8       zhaoy   female  35      189@163.com     17058796503
    9       huangz  male    65      dd@189.com      13648796593
    10      lvbu    male    43      byy@sohu.com    18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803
    [root@lgh test]# awk 'BEGIN{ print "hello work";exit}END{print "god job"}'  #默认会执行END中的代码
    hello work
    god job
    [root@lgh test]# awk 'BEGIN{ print "hello work";;exit flag=1}END{if(flag) exit ;print "god job"}' #使用flag跳出
    hello work

    九、变量

    awk中语句块没有作用域,都是全局变量(除非是函数中的形参)

    变量的赋值:

    可以x=y=z=5,等价于z=5 y=5 x=5
    可以将赋值语句放在任意允许使用表达式的地方
    x != (y = 1)
    awk 'BEGIN{print (a=4);print a}'

    awk中声明变量的位置:

    在BEGIN或main或END代码段中直接引用或赋值
    使用-v var=val选项,可定义多个,必须放在awk代码的前面
    它的变量声明早于BEGIN块
    普通变量:awk -v age=123 'BEGIN{print age}'
    使用shell变量赋值:awk -v age=$age 'BEGIN{print age}'
    
    在awk代码后面使用var=val参数
    它的变量声明在BEGIN之后
        awk '{print n}' n=3 a.txt n=4 b.txt
        awk '{print $1}' FS=' ' a.txt FS=":" /etc/passwd
    使用Shell变量赋值:awk '{print age}' age=$age a.txt

    十、函数和自定义函数

    10.1、自定义函数

    使用function关键字来定义函数:

    function func_name([parameters]){
        function_body
    }

    函数可以定义的位置:

    awk '_ BEGIN{} _ MAIN{} _ END{} _'  #可以定义在任何下划线的地方
    [root@lgh test]# cat a.awk
    BEGIN{
            f()
        }
        function f(){
            print "自定义函数"
        }
    {}#main
    END{}
    [root@lgh test]# echo 0 | awk -f a.awk
    自定义函数
    [root@lgh test]# vim a.awk
    [root@lgh test]# cat a.awk
    function f(){
            print "自定义函数"
        }
    
    BEGIN{
            f()
        }
    {}#main
    END{}
    [root@lgh test]# echo 0 | awk -f a.awk
    自定义函数
    [root@lgh test]# vim a.awk
    [root@lgh test]# cat a.awk
    BEGIN{
            f()
        }
    {}#main
    END{}
    function f(){
            print "自定义函数"
        }
    [root@lgh test]# echo 0 | awk -f a.awk
    自定义函数

    带参数和return的自定义函数:

    [root@lgh test]# cat a.awk
    BEGIN{
            print f(1,2)
        }
    {}#main
    END{}
    function f(a,b){
            print "自定义函数"
            return a+b
        }
    
    [root@lgh test]# echo 0 | awk -f a.awk
    自定义函数
    3

    参数的传递形式:

    传递普通变量时,是按值拷贝传递

    • 直接拷贝普通变量的值到函数中
    • 函数内部修改不会影响到外部

    传递数组时,是按引用传递

    • 函数内部修改会影响到外部

    awk变量作用域:

    • awk只有在函数参数中才是局部变量,其它地方定义的变量均为全局变量。
    • 函数内部新增的变量是全局变量,会影响到全局,所以在函数退出后仍然能访问
    • 函数参数会遮掩全局同名变量,所以在函数执行时,无法访问到或操作与参数同名的全局变量,函数退出时会自动撤掉遮掩,这时才能访问全局变量。所以,参数具有局部效果。
    [root@lgh test]# cat a.awk
    BEGIN{
            a=50 #全局
            print a #50
            print f(a,2) #42
            print a #50
        }
    {}#main
    END{}
    function f(a,b){
            print "自定义函数"
            a=40 #覆盖了全局变量a
            print "局部",a
            return a+b #40+2
        }
    
    [root@lgh test]# echo 0 | awk -f a.awk
    50
    自定义函数
    局部 40
    42
    50

    10.2、getline函数

    getline函数用于从文件、标准输入或管道中读取数据,并按情况设置变量的值。getline可以自动不断的加载下一行。如果能读取记录,则getline的返回值为1,遇到输入流的尾部时,返回值为0,不能读取记录(如文件没有读取权限、文件不存在)时,返回值为“-1"。

    其中:

    1. getline:会从主输入文件中读取记录。会同时设置$0,NF,NR,FNR。
    2. getline var:会从主输入文件中读取记录,并将读取的记录赋值给变量var。会同时设置var,NR,FNR。
    3. getline <file:从外部文件file中读取记录。同时会设置$0,NF。
    4. getline var <file:从外部文件file中读取记录,并将读取的记录赋值给变量var。会同时设置var。
    5. cmd | getline:从管道中读取记录。会同时设置$0,NF。
    6. cmd | getline var:从管道中读取记录,并将读取的记录赋值给变量var。会同时设置var。

    getline与netx的区别
    getline:读取下一行之后,继续执行getline后面的代码
    next:读取下一行,立即回头awk循环的头部,不会再执行next后面的代码

    [root@lgh test]# awk '/^1/{print;getline;print}' test.txt #匹配1开头的行,在1开头的时候,获取到了下一行,也打印了出来
    1       zhangs  female  34      aaa@163.com     13423394013
    2       lisi    male    45      abb@gmail.com   15608492523
    10      lvbu    male    43      byy@sohu.com    18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803
    [root@lgh test]# awk '/^1/{print;getline;print;exit}' test.txt #执行了getline,print,然后exit了
    1       zhangs  female  34      aaa@163.com     13423394013
    2       lisi    male    45      abb@gmail.com   15608492523
    [root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print var}' test.txt #getline赋值给了var
    1       zhangs  female  34      aaa@163.com     13423394013
    2       lisi    male    45      abb@gmail.com   15608492523
    10      lvbu    male    43      byy@sohu.com    18148796803
    11      sunwk   male    99      suk@sohu.com    18148706803
    [root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print $0}' test.txt #getline赋值给了var,但是没有设置$0
    1       zhangs  female  34      aaa@163.com     13423394013
    1       zhangs  female  34      aaa@163.com     13423394013
    10      lvbu    male    43      byy@sohu.com    18148796803
    10      lvbu    male    43      byy@sohu.com    18148796803
    [root@lgh test]# awk '/^2/{while((getline var <"a.awk")>0)print var;close("a.awk")}' test.txt #匹配2开头的行,只有一行,所以后面只执行一次,把a.awk里面的内容赋值给var变量,然后输出
    BEGIN{
            a=50
            print a
            print f(a,2)
            print a
        }
    {}#main
    END{}
    function f(a,b){
            print "自定义函数"
            a=40
            print "局部",a
            return a+b
        }
    
    
    [root@lgh test]# awk '/^2/{while((getline<"a.awk")>0){print};close("a.awk")}' test.txt #匹配2开头的行,只有一行,所以只执行一次,输出a.awk中的内容
    BEGIN{
            a=50
            print a
            print f(a,2)
            print a
        }
    {}#main
    END{}
    function f(a,b){
            print "自定义函数"
            a=40
            print "局部",a
            return a+b
        }
    [root@lgh test]# awk '/^2/{"date"|getline;print}' test.txt #使用linux的date命令,通过管道,使用getline读取
    Tue Nov 24 19:44:28 CST 2020
    [root@lgh test]# awk '/^2/{"date"|getline var ;print var}' test.txt
    Tue Nov 24 19:44:41 CST 2020
    [root@lgh test]# awk '/^1/{"date"|getline var ;print var}' test.txt
    Tue Nov 24 19:44:48 CST 2020
    Tue Nov 24 19:44:48 CST 2020
    Tue Nov 24 19:44:48 CST 2020

    awk虽然强大,但是有些数据仍然不方便处理,这时可将数据交给Shell命令去帮助处理,然后再从Shell命令的执行结果中取回处理后的数据继续awk处理。awk通过|&符号来支持coproc。

    awk_print[f] "something" |& Shell_Cmd
    Shell_Cmd |& getline [var]

    这表示awk通过print输出的数据将传递给Shell的命令Shell_Cmd去执行,然后awk再从Shell_Cmd的执行结果中取回Shell_Cmd产生的数据

    [root@lgh test]# cat a.awk
    BEGIN{
          CMD="sed -nr "s/.*@(.*)$/\1/p""; #获取邮箱@符号后面的内容
        }
        NR>1{
            print $5; #输出邮箱名
            print $5 |& CMD; #截取邮箱@符号后面的内容
            close(CMD,"to");
            CMD |& getline email_domain; #把邮箱@后面的内容赋值给变量email_domain
            close(CMD);
            print email_domain; #输出email_domain
    
    
    }
    [root@lgh test]# awk -f a.awk test.txt
    aaa@163.com
    163.com
    abb@gmail.com
    gmail.com
    ccc@sohu.com
    sohu.com
    acc@sohu.com
    sohu.com
    gdd@163.com
    163.com
    adb@139.com
    139.com
    eab@189.com
    189.com
    189@163.com
    163.com
    dd@189.com
    189.com
    byy@sohu.com
    sohu.com
    suk@sohu.com
    sohu.com

    10.3、内置字符串函数

    index(str1,str2):返回子串str2在字符串str1中第一次出现的位置。如果没有指定str1,则返回0。
    length(str1):返回字符串str1的长度。如果未给定str1,则表示计算"$0"的长度。
    substr(str1,p):返回str1中从p位置开始的后缀字符串。
    substr(str1,p,n):返回str1中从p位置开始,长度为n的子串。
    match(str1,regexp):如果regexp能匹配str1,则返回匹配起始位置。否则返回0。它会设置内置变量RSTART和RLENGTH的值。
    split(str1,array,sep):使用字段分隔符sep将str1分割到数组array中,并返回数组的元素个数。如果未指定sep则采用FS的值。因此该函数用于切分字段到数组中,下标从1开始。
    sprintf(fmt,expr):根据printf的格式fmt,返回格式化后的expr。
    sub(regexp,rep,str2):将str2中第一个被regexp匹配的字符串替换成rep,替换成功则返回1(表示替换了1次),否则返回0。注意是贪婪匹配。
    sub(regexp,rep):将"$0"中第一个被regexp匹配的字符串替换成rep,替换成功则返回1,否则返回0。注意是贪婪匹配。
    gsub(regexp,rep,str2):将str2中所有被regexp匹配的内容替换成rep,并返回替换的次数。
    gsub(regexp,rep):将"$0"中所有被regexp匹配的内容替换成rep,并返回替换的次数。
    toupper(str):将str转换成大写字母,并返回新串。
    tolower(str):将str转换成小写字母,并返回新串。

    10.4、内置数值函数

    int(expr)     截断为整数:int(123.45)和int("123abc")都返回123,int("a123")返回0
    sqrt(expr)    返回平方根
    rand()        返回[0,1)之间的随机数,默认使用srand(1)作为种子值
    srand([expr]) 设置rand()种子值,省略参数时将取当前时间的epoch值(精确到秒的epoch)作为种子值

    参考:

    https://www.cnblogs.com/f-ck-need-u/p/7509812.html#9-awk-

    https://www.cnblogs.com/f-ck-need-u/p/12688355.html

    https://www.cnblogs.com/hdk1993/p/4637525.html

  • 相关阅读:
    11.MySQL
    10、设计模式
    9
    8
    7
    6
    5
    4
    3
    2
  • 原文地址:https://www.cnblogs.com/zsql/p/14030560.html
Copyright © 2011-2022 走看看