这篇文章上次修改于 1705 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

Awk

Awk简介

  • Awk是一种编程语言,可以在Linux下对文本和数据进行扫描与处理,数据可以来自标准输入、文件、管道。

    Awk工作流程

  • 逐行扫描文件 --> 从第一行到最后一行 --> 寻找匹配特定模式的行 --> 进行用户操作
  • Awk的两个特殊模式:

    1. BEGIN模式:awk将在读取任何输入行之前执行BEGIN中的操作。
    2. END模式:awk将在正式退出之前执行END中的操作。

      Awk基本语法格式

      gawk [选项] -f program-file [--] file ...
      POSIX options:          GNU long options: (standard)
         -f progfile             --file=progfile
         -F fs                   --field-separator=fs
         -v var=val              --assign=var=val
      Short options:          GNU long options: (extensions)
         -b                      --characters-as-bytes
         -c                      --traditional
         -C                      --copyright
         -d[file]                --dump-variables[=file]
         -e 'program-text'       --source='program-text'
         -E file                 --exec=file
         -g                      --gen-pot
         -h                      --help
         -L [fatal]              --lint[=fatal]
         -n                      --non-decimal-data
         -N                      --use-lc-numeric
         -O                      --optimize
         -p[file]                --profile[=file]
         -P                      --posix
         -r                      --re-interval
         -S                      --sandbox
         -t                      --lint-old
         -V                      --version

      Awk操作指令

      记录与字段

  • Awk一次从文件中读取一条记录,并将记录存储在字段变量$0中。记录为分割为字段并存储在$1,$2,...,$NF中(默认使用空格或制表符为分隔符)。

    #读取输入行,并输出第一个字段、第二个字段、第三个字段
    [root@Hyui-VM ~]# echo hello the world | awk '{print $1,$2,$3}'
    hello the world
    #读取输入行,直接输出该行
    [root@Hyui-VM ~]# echo hello the world | awk '{print $0}'
    hello the world
    #输出该行的字段个数
    [root@Hyui-VM ~]# echo hello the world | awk '{print NF}'
    3
    #输出该行最后一个字段
    [root@Hyui-VM ~]# echo hello the world | awk '{print $NF}'
    world

    字段分隔符

  • Awk默认使用空格和制表符作为分隔符,使用-F和FS可以改变分隔符。

    [root@Hyui-VM ~]# awk -F: '{print $1}' /etc/passwd
    [root@Hyui-VM ~]# awk 'BEGIN {FS=":"} {print $1}' /etc/passwd
  • 以上两种方法都是将":"设定为分隔符,以":"为分隔符打印passwd文件的第一个字段。
  • 如何指定多个字段分隔符?

    [root@Hyui-VM ~]# echo 'hello the:world,!' | awk 'BEGIN {FS="[:,]"} {print $1,$2,$3,$4}'
    hello the world !

    内置变量

    变量列表

    变量名称描 述
    ARGC命令行参数个数
    FILENAME当前输入文档名称
    FNR当前输入文档当前记录编号
    NR输入流的当前记录编号
    NF当前记录的字段个数
    FS字段分隔符
    OFS输出字段分隔符,默认为空格
    ORS输出记录分隔符,默认换行符\n
    RS输入记录分隔符,默认为\n

    示例1

    [root@Hyui-VM ~]# cat neko.html 
    <html>
    <title>Neko Site</title>
    <body>
    h1Cute Nekoh1
    h2Kawaii Nekoh2
    h3Yasashi Nekoh3
    </body>
    </html>
    [root@Hyui-VM ~]# cat network 
    DEVICE=eno16777736
    onboot=yes
    BOOTPROTO=static
    ipaddr=192.168.0.1
    nEtMaSt=255.255.255.0
    GaTeWaY=192.168.0.254
  • 输出当前文档的当前编号,第一个文件有8行,第二个文件有6行。

    [root@Hyui-VM ~]# awk '{print FNR}' neko.html network 
    1
    2
    3
    4
    5
    6
    7
    8
    1
    2
    3
    4
    5
    6
  • 将两个文档作为一个整体输入流,通过NR输入当前编号。

    [root@Hyui-VM ~]# awk '{print NR}' neko.html network 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • 文档每行的字段数量。

    [root@Hyui-VM ~]# awk '{print NF}' neko.html 
    1
    2
    1
    2
    2
    2
    1
    1

    示例2

  • 通过OFS将输出分隔符设置为"-"。

    [root@Hyui-VM ~]# cat test.txt 
    This is a test file.
    Welcome to CentOS Linux 7.
    root@Hyui-VM ~]# awk 'BEGIN {OFS="-"} {print $1,$2,$3}' test.txt 
    This-is-a
    Welcome-to-CentOS

    表达式与操作符

  • 操作符示例

    [root@Hyui-VM ~]# echo "test" | awk 'x=2 {print x+3}'
    5
  • 9

    输出sshd_config所有空白行

    [root@Hyui-VM ~]# awk '/^$/ {print x+=1}' /etc/ssh/sshd_config
    1
    2
    3
    4
    5
    ......[省略]

    输出sshd_config总空白行个数

    [root@Hyui-VM ~]# awk '/^$/ {x+=1} END {print x}' /etc/ssh/sshd_config
    27

    列出passwd的id大于500的用户名

    [root@Hyui-VM ~]# awk -F: '$3>500 {print $1}' /etc/passwd
    polkitd
    chrony

    ## Awk高级应用
  • 格式:

    if (表达式)
    动作1
    else
    动作2
    
    if (表达式) 动作1; else 动作2
  • 判断vda1可用存储是否不足2G

    [root@Hyui-VM ~]# df | grep vda1 | awk '{if($4<2000000) Print "Insufficient storage"; else print "OK"}'
    OK

    While循环

  • 格式1:

    while(条件)
    动作
  • 示例:

    [root@Hyui-VM ~]# awk 'i=1 {} BEGIN {while(i<=5){++i; print i}}' neko.html 
    1
    2
    3
    4
    5
    6
  • 格式2:

    do
    动作
    while(条件)
  • 示例:

    [root@Hyui-VM ~]# awk 'BEGIN { do {++x; print x} while (x<=5)}' neko.html 
    1
    2
    3
    4
    5
    6

    For循环

    for (变量;条件;计数器)
    动作
  • 示例:

    [root@Hyui-VM ~]# awk 'BEGIN {for (i=1;i<=5;i++) print i}' neko.html 
    1
    2
    3
    4
    5

    函数

    rand()函数

  • 作用:产生0~1之间的浮点类型随机数。
  • 示例:

    [root@Hyui-VM ~]# awk 'BEGIN{print rand(); srand(); print srand()}' test.txt 
    0.237788
    1585732218
  • 使用srand()使每次产生的随机数不同

    gsub(x,y,z)函数

  • 作用:在字串z中使用字串y替换与正则表达式x相匹配的所有字串,z默认为$0。

    sub(x,y,z)函数

  • 作用:在字串z中使用字串y替换与正则表达式x相匹配的di'yi'g字串,z默认为$0。

    [root@Hyui-VM ~]# awk -F: 'gsub(/root/,"centos",$0) {print $0}' /etc/passwd 
    centos:x:0:0:centos:/centos:/bin/bash
    operator:x:11:0:operator:/centos:/sbin/nologin
    [root@Hyui-VM ~]# awk -F: 'sub(/root/,"centos",$0) {print $0}' /etc/passwd 
    centos:x:0:0:root:/root:/bin/bash
    operator:x:11:0:operator:/centos:/sbin/nologin

    length(z)函数

  • 作用:计算并返回字串长度。

    [root@Hyui-VM ~]# awk '{print length()}' test.txt 
    20
    26

    getline函数

  • 作用:从输入中读取下一行内容。

    [root@Hyui-VM ~]# df -h
    Filesystem      Size  Used Avail Use% Mounted on
    devtmpfs        1.9G     0  1.9G   0% /dev
    tmpfs           1.9G     0  1.9G   0% /dev/shm
    tmpfs           1.9G  424K  1.9G   1% /run
    tmpfs           1.9G     0  1.9G   0% /sys/fs/cgroup
    /dev/vda1        40G  2.1G   36G   6% /
    tmpfs           379M     0  379M   0% /run/user/0
    [root@Hyui-VM ~]# df -h | awk '{if(NF==1) {getline; print $3}; if(NF==6) print $4}'
    1.9G
    1.9G
    1.9G
    1.9G
    36G
    379M