一条命令带你了解awk高阶用法


前言

优化shell脚本,主要是通过shell获取主机资源使用率,其中磁盘I/O繁忙使用了iostat命令取最后一列,也就是util列。

在这里插入图片描述

一、原始命令分析

iostat -k -x 1 1 | sed -n '/Device/,/Device/!p' | sed '1,5d' | egrep -v "^$"

其实单看这条命令没有什么问题,但是iostat命令取第一次的值通常是主机启动到目前的均值,不会很直观反应当前的状态,所以打算取2次,取第2次的值。

另外,过度依赖sed和grep对特定输出格式进行解析的脚本是非常脆弱的,所以打算修改获取方式,使用更强大工具——awk

二、awk实现

使用awk命令,如下:

iostat -k -x 1 2 | awk '/^Device/ {count++} count==2 && !/^Device/'

命令解析

  • /Device/ {count++}:这是一个模式动作对。当 awk 读到包含 “Device” 的行时,它就将一个我们自己定义的变量 count 的值加 1。

  • count==2:这是一个模式,不带动作。当 count 的值等于 2 时(即我们已经进入了第二个统计块),这个条件就为真。awk 的默认动作是打印整行 (print $0)。

  • count==2 && !/Device/:最后又增加了一个条件 !/Device/,意思是“并且不包含 "Device"开头的行”。这样就巧妙地跳过了第二个统计块的表头行,只打印纯粹的数据行。

三、高阶用法

除了常用的'{print $0,$NF,$NR}'打印某一列,-F参数指定分隔符外,awk还有很多便捷易用的功能。详细的用法可以参考这篇文章,文本处理三剑客awk

awk 工作模式

awk的它的核心结构

pattern { action }

awk 会逐行读取输入内容,对于每一行,它都会:

  1. 用 pattern (模式) 去匹配。

  2. 如果当前行匹配 pattern,awk 就执行 action (动作) 中定义的命令。

这个结构有几个重要的变种:

  1. 只有 { action }: 如果没有 pattern,那么 action 会对每一行都执行。

  2. 只有 pattern: 如果一个 pattern 后面没有 { action },awk 会执行一个默认动作,即打印整行内容 (print $0)。

  3. 多个 pattern { action }: 您可以在一个 awk 命令中写多组成对的 pattern { action },awk 会对每一行依次检查所有模式。

awk模式

1. 正则表达式模式 (Regular Expression Pattern)

使用斜杠 / 包裹。

  • 语法: /正则表达式/
  • 作用: 对当前处理的行进行正则表达式匹配。如果匹配成功,则执行后续的 { action }
  • 示例:
    # 打印所有包含 "error" 字符串的行
    awk '/error/' /var/log/messages
    

2. 关系/条件表达式模式 (Relational/Conditional Expression Pattern)

直接使用编程语言中的逻辑判断。

  • 语法: expression (例如 NF > 3, $1 == "user", my_var == 1)
  • 作用: 计算表达式的值。如果值为“真”(即非零或非空字符串),则执行后续的 { action }
  • 示例:
    # 打印 /etc/passwd 文件中,用户ID(第三个字段)大于1000的行
    awk -F: '$3 > 1000' /etc/passwd
    

3. 范围模式 (Range Pattern)

这是一种非常强大的模式,用于处理跨越多行的文本块。

  • 语法: pattern1, pattern2
  • 作用: 这个模式会匹配一个范围内的所有行。它从第一个匹配 pattern1 的行开始,到下一个匹配 pattern2 的行结束(包括这两行)。
  • 示例:
    # 打印日志文件中,从包含 "START TRANSACTION" 的行开始,到包含 "END TRANSACTION" 的行结束之间的所有内容
    awk '/START TRANSACTION/,/END TRANSACTION/' app.log
    

4. 特殊模式 BEGINEND

这是 awk 的两个“钩子”(hook),它们不匹配任何输入行,而是在特定的时间点执行。

  • BEGIN:

    • 语法: BEGIN { action }
    • 作用: BEGIN 块中的 action 会在 awk 开始处理任何输入行之前执行,并且只执行一次。
    • 用途: 非常适合用来初始化变量、打印报表表头等。
  • END:

    • 语法: END { action }
    • 作用: END 块中的 action 会在 awk 处理完所有输入行之后执行,并且只执行一次。
    • 用途: 非常适合用来进行最终的计算、打印总计和报表结尾。
  • 示例:

    # 计算文件中所有数字的总和
    echo -e "10\n20\n30" | awk '
        BEGIN { sum=0; print "开始计算..." }
        { sum += $1 }
        END { print "计算完成,总和为:", sum }'
    

    输出:

    开始计算...
    计算完成,总和为: 60
    

5. 组合模式 (Compound Pattern)

通过逻辑运算符将上述各种模式组合起来,形成更复杂的匹配条件。

  • 语法:
    • pattern1 && pattern2 (逻辑与 AND)
    • pattern1 || pattern2 (逻辑或 OR)
    • ! pattern (逻辑非 NOT)
  • 作用: 创建复合逻辑判断。
  • 示例:
    # 打印第三个字段大于100,并且行中包含 "admin" 的行
    awk -F: '$3 > 100 && /admin/' /etc/passwd
    

四、总结

最后大家应该理解到awk处理文本的强大,它内置的变量、逻辑判断和模式匹配能力,使其能够用非常少的代码替代复杂的命令链,处理结构化的文本数据。
就像在我本次修改的脚本中,获取util最高的设备,使用awk一条命令就可以实现,因为awk本身自带循环能力(逐行读取),再加上可以使用判断、变量不需要定义直接使用等特点,让逻辑代码变得非常简洁。而且使用awk命令看起来比使用一堆管道符加grephead等命令高级。

iostat -k -x 1 2 | awk '
    # 找到第三个 "Device" 块 (即第二次的实时采样)
    /^Device/ {count++}
    count==2 && !/^Device/ {
        # 如果当前行的最后一个字段 ($NF) 大于我们记录的 max 值
        # 就更新 max 的值为当前行的最后一个字段
        if ($NF > max) {
            max = $NF
        }
    }
    END {
        # 所有行处理完毕后,打印最终记录的 max 值
        # 如果没有设备数据,max 默认为0,打印出来也是0
        print max
    }
'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偷学技术的梁胖胖yo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值