文本处理
awk

一、awk 概述

(一)awk 的起源与定义

awk 由 Alfred Aho、Peter Weinberger 和 Brian Kernighan 共同开发,名字取自他们三人姓氏的首字母。它是一款用于处理和操作文本文件的命令行工具,在文本处理领域应用广泛 。

(二)awk 的用途

awk 主要用于从文本数据中提取特定信息、对数据进行转换和处理,以及生成格式化的输出。在系统管理、数据分析、日志处理等场景中,经常会用到 awk 来处理文本文件,帮助用户快速获取和分析所需的数据 。

二、awk 的常用例子

(一)提取 CPU idle 信息

#top -n 参数指定了top命令的执行次数。如果不指定次数,top 命令会一直执行,从而无法输出到管道
top -n 1
#awk -F":|,", 以:或,为分隔符
top -n 1 | grep %Cpu | awk -F":|," '{print $5}' 

(二)合并特定行内容

使用Ubunut 官方软件仓库配置文件作为示范

cat /etc/apt/sources.list.d/* |grep -v -E "^#|^$"
Types: deb
URIs: http://archive.ubuntu.com/ubuntu
Suites: oracular oracular-updates oracular-backports
Components: main universe restricted multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIs: http://security.ubuntu.com/ubuntu
Suites: oracular-security
Components: main universe restricted multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

对于ubuntu官方软件仓库配置文件,将匹配到的Types和和下面的每一行合并为同一行输出,直到匹配到下一个Types

#其原理是,当匹配到`Types`开头的行时,将该行内容存储到变量`type`中,然后使用next跳过当前行的后续处理;接着对下一行,将变量`type`的值与该行内容用"  ----  "连接并打印出来 。
cat /etc/apt/sources.list.d/* |grep -v -E "^#|^$"|awk '/^Types:/{type=$0;next;}{print type "  ----  " $0}'
Types: deb  ----  URIs: http://archive.ubuntu.com/ubuntu
Types: deb  ----  Suites: oracular oracular-updates oracular-backports
Types: deb  ----  Components: main universe restricted multiverse
Types: deb  ----  Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb  ----  URIs: http://security.ubuntu.com/ubuntu
Types: deb  ----  Suites: oracular-security
Types: deb  ----  Components: main universe restricted multiverse
Types: deb  ----  Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

三、awk 整体模式

(一)模式结构

awk 的整体模式为awk 'BEGIN{ commands } pattern{ commands } END{ commands }'

BEGIN{ commands }:只会在处理文本之前执行一次,通常用于变量初始化、打印表头信息等操作 。

pattern{ commands }:以行为单位对文本进行处理,每行都会匹配pattern,如果匹配成功,则执行对应的commands

END{ commands }:只会在处理完所有文本后执行一次,常用于统计结果打印等操作 。

(二)模式示例

#执行时首先会执行BEGIN部分,打印出BEGIN;然后逐行处理top -n 1的输出结果,每行都原样打印;最后执行END部分,打印出END 
top -n 1 | awk 'BEGIN{ print "BEGIN" }{ print $0 } END{ print "END" }'
BEGIN
 
top - 06:47:59 up 3 days, 21:33,  3 users,  load average: 0.00, 0.00, 0.00
Tasks: 108 total,   1 running, 107 sleeping,   0 stopped,   0 zombie
%Cpu(s):  8.3 us,  8.3 sy,  0.0 ni, 83.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
MiB Mem :   1967.5 total,    137.4 free,   1323.2 used,   1631.9 buff/cache     
MiB Swap:      0.0 total,      0.0 free,      0.0 used.    644.3 avail Mem 
 
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND  
      1 root      20   0   23196  11236   6628 S   0.0   0.6   0:16.86 systemd  
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.04 kthreadd 
      3 root      20   0       0      0      0 S   0.0   0.0   0:00.00 pool_wo+ 
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
      5 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
      7 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
     10 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
     12 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
     13 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tas+ 
     14 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tas+ 
     15 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tas+ 
     16 root      20   0       0      0      0 S   0.0   0.0   0:01.33 ksoftir+ 
     17 root      20   0       0      0      0 I   0.0   0.0   0:03.68 rcu_pre+ 
     18 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_exp+ 
     19 root      20   0       0      0      0 S   0.0   0.0   0:00.01 rcu_exp+ 
     20 root      rt   0       0      0      0 S   0.0   0.0   0:01.40 migrati+ 
 
 
END

(三)常用命令示例

使用top命令的结果作为素材

top -n 1 | tail -n 19|head -n 17
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND  
      1 root      20   0   23196  11236   6628 S   0.0   0.6   0:16.98 systemd  
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.04 kthreadd 
      3 root      20   0       0      0      0 S   0.0   0.0   0:00.00 pool_wo+ 
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
      5 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
      7 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
     10 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
     12 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker+ 
     13 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tas+ 
     14 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tas+ 
     15 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tas+ 
     16 root      20   0       0      0      0 S   0.0   0.0   0:01.34 ksoftir+ 
     17 root      20   0       0      0      0 I   0.0   0.0   0:03.71 rcu_pre+ 
     18 root      20   0       0      0      0 S   0.0   0.0   0:00.00 rcu_exp+ 
     19 root      20   0       0      0      0 S   0.0   0.0   0:00.01 rcu_exp+ 

1.提取USER列数据,由于top 输出内容的行首存在分隔符,所以第一列为空,PID属于第二列,USER属于第三列,$0是整行,所以$1是第一列。

top -n 1 | tail -n 19|head -n 17|awk '{print $3}'

2.条件表达式筛选符合特定条件的行,并对其进行处理。筛选出PID大于15的行:

top -n 1 | tail -n 19|head -n 17|awk '$2 > 15 {print $0}'

四、awk 关键字及用法

(一)NR

NR表示当前处理的行号。例如NR > 5表示从第 5 行开始处理文本 。在实际应用中,可用于跳过文件开头的一些无关行,只处理需要的数据行。

top -n 1 | tail -n 19|head -n 17|awk 'NR > 5 {print $0}'

(二)NF

NF表示当前行的字段数。比如,要查看每行有多少个字段,可以使用:

top -n 1 | tail -n 19|head -n 17|awk '{print NF}'

(三)FILENAME

FILENAME用于获取当前处理的文件名。在多文件处理时,可通过该变量区分不同文件的数据来源,如前面多文件处理示例中所示 。

awk -F':' '/^root/{print FILENAME "  ----  " $0}' /etc/passwd /etc/group
/etc/passwd  ----  root:x:0:0:root:/root:/bin/bash
/etc/group  ----  root:x:0:

(四)OFS

OFS是输出字段分隔符(Output Field Separator),用于指定输出内容的分隔符 。默认情况下,输出字段之间以空格分隔,当设置OFS="-"时,输出内容会以-为分割符 。 默认的输出分隔符为空格

awk -F':' 'NR < 5 {print $1,$2,$3}' /etc/passwd

设置输出分隔符为-

awk -F':' 'NR < 5 {print $1,$2,$3}' OFS="-" /etc/passwd

五、更多 awk 应用示例

(一)处理 top 命令输出

提取特定列信息:

top -n 1 -b | awk 'NR > 7' | awk '{print $1, $9, $10, $12}' OFS="-"
top -n 1 -b | awk 'NR > 7' | awk '{printf("%7s - %4s - %4s - %s\n", $1, $9, $10, $12)}'

这两个命令,都是先舍弃top -n 1 -b输出结果的前 7 行数据,然后按空格隔开,取第 1、9、10、12 列数据 。不同的是,第一个命令使用OFS指定输出分隔符为-,第二个命令使用printf函数进行格式化输出 。

分析过程:top -n 1的输出结果包含了系统中各个进程的资源使用情况等信息,前 7 行通常是一些表头和系统整体信息,不是我们关注的进程数据 。通过NR > 7跳过前 7 行,再选取特定列的数据进行输出,就可以得到我们想要的进程相关信息,如 PID、% CPU、% MEM、COMMAND 等 。

(二)统计平均 sys 时间

top -n 121 -d 1 -b | grep %Cpu | tail -n 120 > /tmp/cpu.log

这条命令用于记录 121 次top信息中的 CPU 利用率部分内容,并取后 120 次保存至/tmp/cpu.log中 。

cat /tmp/cpu.log | awk '{sti+=$4}END{print "120秒钟top信息sy时间的平均值:"sti/NR}'

对/tmp/cpu.log中的每一行内容以空格进行分割,取第 4 个数据(即sy时间)进行累加,sti记录的是sy time的时间总和,NR表示总行数 。完成所有行数据操作后,将累加和除以总行数NR,得到 120 秒钟内平均的sys时间 。

实际意义:在系统性能分析中,了解 CPU 的sys时间平均值可以帮助管理员评估系统内核在处理系统调用等操作时的负载情况,判断系统是否存在性能瓶颈 。