千家信息网

awk命令与Zabbix自定义参数的介绍

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,awk 命令awk是一种处理文本文件的语言,是一个强大的文本分析公具。awk处理文本和数据的方式:逐行读入文本,寻找匹配特定模式的行,然后进行操作。输出文件匹配行的特定字段功能很强大,所以有很多用处。
千家信息网最后更新 2026年10月31日awk命令与Zabbix自定义参数的介绍

awk 命令

awk是一种处理文本文件的语言,是一个强大的文本分析公具。
awk处理文本和数据的方式:逐行读入文本,寻找匹配特定模式的行,然后进行操作。

输出文件匹配行的特定字段

功能很强大,所以有很多用处。这里我主要关注下面这样的场景:
逐行读入文本,按规则匹配特定的行,以空格为默认分隔符将每行切片,输出其中特定的某个切片(切开的部分可以进行各种分析处理,这里就是要输出其中以段):

$ cat /etc/hosts127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4::1         localhost localhost.localdomain localhost6 localhost6.localdomain6$ awk '/local/ {print $1}' /etc/hosts127.0.0.1::1$ 

这种方法很适合用来做zabbix的自定义key的监控。比如从free命令中,提取出内存的使用量:

$ free              total        used        free      shared  buff/cache   availableMem:        1855432      320688     1238808       10612      295936     1495432Swap:       2093052           0     2093052$ free | awk '/^Mem:/ {print $3}'320688$ 

grep命令
同样的效果,也可以通过grep命令来把需要的行过滤出来,然后还得借助cut命令来进行列切割。
但是使用awk的话就一步搞定了。

内置变量

内置变量先列出来,后面会用到其中一些。
awk内置变量:

  • ARGC: 命令行参数个数
  • ARGV: 命令行参数排列
  • ENVIRON: 支持队列中系统环境变量的使用
  • FILENAME: awk浏览的文件名
  • FNR: 浏览文件的记录数
  • FS: 设置输入域分隔符,等价于命令行 -F选项
  • IGNORECASE: 如果把这个变量设为1,则正则表达式忽略大小写
  • NF: 浏览记录的域的个数
  • NR: 已读的记录数
  • OFS: 输出域分隔符
  • ORS: 输出记录分隔符
  • RS: 控制记录分隔符
  • $0: 变量是指整条记录
  • $1: 表示当前行的第一个域,$2表示当前行的第二个域,......以此类推
  • $NF: 既然 NF是列的总数,那么$NF就是最后一列的值
  • $NF-1: 这个是倒数第二列,其他以此类推

上面这些变量,有些是直接来使用的。比如$1,$NF,后面的例子中会用到,也比较好理解。
还有些是用来改变awk行为的,需要对变量进行设置,这个需要会为变量赋值,有多种方式可以实现。
比如FS,是用来指定分隔符的,默认的分隔符是空白符,但是可以指定。这就需要自己定义FS的值。不过分隔符还提供了一个 -F 选项来定义。所以也可以在命令行选项中设置。
但是其他一些变量需要指定,但又没有提供别的方法的话,就只能用过为变量赋值来实现了。
分隔符和为变量赋值的方式在后面会展开,为变量赋值参考自定义变量的内容。

分隔符

默认awk是以空白符来做分隔的。使用 -F 选项可以自定义分隔符:

$ grep -e "^root" /etc/passwdroot:x:0:0:root:/root:/bin/bash$ awk -F: '/^root/ {print $1,$NF}' /etc/passwdroot /bin/bash$ 

这里将分隔符指定为冒号。

多分隔符
默认的也是多分隔符的情况,空格、制表符等都会被识别。自己要指定多个分隔符,则是用中括号把需要识别的分隔符都括起来:

$ echo "a-b_c=d-E_F=G" | awk -F[-_=] '{print $1,$2,$3,$4,$5,$6,$7}'a b c d E F G$ 

过滤连续的分隔符
-F 选项也是支持正则表达式的,中括号就是正则表达式字符集合的意思。但是如果这时遇到连续的分隔符,就会有问题。下面使用逗号和空格作为分隔符,并且每次都连续出现:

$ echo "a,,b  c" | awk -F'[ ,]' '{print $1"-"$2"-"$3}' a--b$ 

正则表达式中匹配一次或多次,使用加号后,就可以了:

$ echo "a,,b  c" | awk -F'[ ,]+' '{print $1"-"$2"-"$3}'a-b-c$ 

特殊字符分隔符
特殊字符应该就是这些: $、^、*、(、)、[、]、?、.、|
单独作为分隔符并没有问题:

$ echo '1a$1b$1c' | awk -F'$' '{print $1"-"$2"-"$3}'   1a-1b-1c$ 

如果指定多个字符作为一个整体作为一个分隔符,就会有问题,需要转义。比如这里要将 $1 作为分隔符:

$ echo '1a$1b$1c' | awk -F'$1' '{print $1"-"$2"-"$3}'1a$1b$1c--$ echo '1a$1b$1c' | awk -F'\\$1' '{print $1"-"$2"-"$3}'1a-b-c$ 

再来个多个特殊字符组合的:

$ echo 'a$|b$|c' | awk -F'\\$\\|' '{print $1"-"$2"-"$3}'a-b-c$ 

默认分隔符
默认就是空白符作为分隔符,并且能够识别连续的空白符。默认分隔符就是下面的这个正则表达式:

FS="[[:space:]+]" 

看上面的内置变量,FS和-F选项是等价的。

格式化输出printf

除了用print,还可以用printf做格式化输出。这里就给出一个例子,关于printf格式化输出,需要的话再去参考下C语言的printf的功能把。

一般都用print输出:

$ awk -F: '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd     filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bashfilename:/etc/passwd,linenumber:2,columns:7,linecontent:bin:x:1:1:bin:/bin:/sbin/nologinfilename:/etc/passwd,linenumber:3,columns:7,linecontent:daemon:x:2:2:daemon:/sbin:/sbin/nologin

对比下用printf格式化输出后的效果:

$ awk -F: '{printf ("filename:s, linenumber:%3s,column:%3s,content:%3f\n",FILENAME,NR,NF,$0)}' /etc/passwdfilename:/etc/passwd, linenumber:  1,column:  7,content:0.000000filename:/etc/passwd, linenumber:  2,column:  7,content:0.000000filename:/etc/passwd, linenumber:  3,column:  7,content:0.000000

BEGIN 和 END 模块

通常,对于每个输入行,awk 都会执行一次脚本代码块。
有时,需要在 awk 开始处理输入文件中的文本之前执行初始化代码。这就需要定义一个 BEGIN 块。
另外,还有一个 END 块,用于执行最终计算或打印应该出现在输出流结尾的摘要信息。

在BEGIN块中定义内置变量
这里在BEGIN块中定义了两个内置变量:

$ echo "a,,b   c" | awk 'BEGIN{FS="[ ,]+";OFS="-"}{print $1,$2,$3}' a-b-c$ 

FS是分隔符,OFS是输出字段分隔符。在之前的例子中,不用BEGIN块也是能实现这个效果的。
这里修改的是一个内置变量,但是方法是针对变量的,包括自定义变量。具体参考下一章"awk自定义变量"。

这里主要挑BEGIN块举例用法。END块可以实现计算统计输出的功能,暂时用不上,略过。

awk自定义变量

除了内置变量,也可以定义自定义变量并使用。这部分内容对于灵活的配置非常有用,而且如果自己写,也会遇到一些坑。

定义在后面

这里变量的赋值在发生在BEGIN块执行之后的
直接写在后面:

$ echo | awk '{print key1,key2}' key1=v1 key2=V2v1 V2$ 

这种用法在BEGIN块中是识别不了变量的。BEGIN块的执行在这些变量定义之前。不过还有其他的方法可以用。
另外,这里使用管道作为标准输入。如果是从文件输入的话,文件路径在写最后。

在BEGIN块中定义

这里变量额赋值是在BEGIN块执行的时候
在BEGIN块中可以对内置变量赋值,同样的也可以为自定义变量赋值

$ echo | awk 'BEGIN{key1="v1";key2="value2";OFS="_"}{print key1,key2}' v1_value2$ 

使用 -v 选项

这里变量的赋值是在BEGIN块执行之前
这个方法在发生在BEGIN块执行之前的:

$ echo | awk -v key1=V1 -v key2=value2 '{print key1,key2}'V1 value2$ 

如果是多个变量,则使用 -v 多次。

如果把上面两个方法合起来:

$ echo | awk -v key1=v1 -v key2=v2 'BEGIN{print "BEGIN: "key1,key2}{print "ACTION: "key1,key2}' key1=VALUE1 key2=VALUE2      BEGIN: v1 v2ACTION: VALUE1 VALUE2$  

先是 -v 进行赋值,然后BEGIN块执行。之后是最后的变量赋值,如果有同名的就替换值,之后再逐行执行。打印出来的就是之后改变的值。

打印环境变量

最好的方法在后一小节。这里的方法也是可行的,但是可读性不好。
写这段是为了理解一下命令参数解析的过程,以及一些特殊情况的处理。
要直接打印环境变量是这样的:

$ echo | awk  '{print "'"$PATH"'"}'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin$ 

这里的显示不明显,两边都是一对双引号套一个单引号,看图

解释说明
先用一个简单点的环境变量来举例:

$ echo $USERroot$ 

这个没有什么空格换特殊字符,这样可以去掉最里面的一对双引号:

$ echo | awk  '{print "'$USER'"}'  root$ 

这里成对出现了2对单引号,所以就被分成了这样两个部分:awk '{print 和 '"}'。awk对2个单引号内的命令起作用。
剩下的就是 $USER 了,这个最早就被 shell 给处理替换了。
在变量本身被shell处理完之后,如果有空格之类的,有会被认为不是一个部分。这里就再用双引号把环境变量的值包起来,将值作为整体的一个域。

我的理解
最外层的引号是用来界定字符边界的,但是只要是连续的就被系统认为是一串(一个域)。可以用多对引号把多个字符串引起来,但是每对引号之间不要出现分隔符。这样,最后解析交给命令处理的还是一个整体的字符串(一个域)。
下面是用echo命令的演示:

$ echo 'abc''def'abcdef$ echo 'abc'$USER'def'abcrootdef$ echo 'abc '$USER' def'abc root def$ 

加上for循环再演示一次:

$ HELLO='Hello World !'$ for i in $HELLO; do echo $i;doneHelloWorld!$ for i in 'BEFOR'$HELLO'AFTER'; do echo $i;doneBEFORHelloWorld!AFTER$ for i in 'BEFOR'"$HELLO"'AFTER'; do echo $i;doneBEFORHello World !AFTER$ 

最外层的引号仅仅是界定边界的,用多对引号但是所有内容都相连,也被认为是一个域。

虽然有多对引号,但是所有内容都是相连的,没有分隔符,最后交给命令处理的还是一个域。
这样做的好处就是,用了单引号,但是把需要shell解析的部分放到了单引号的外面,这样shell还是可以正常解析。
为了保证环境变量解析完之后依然是一个域,需要用双引号引起来。
再来就是awk中接着print的双引号了。awk中的引号不是界定边界的而是区分是变量还是字符串的。没有双引号的话表示这个内容是变量,用双引号引起来表示里面的内容是字符串,直接打印。

其他写法
下面两种写法也能实现同样的效果,帮助理解吧:

$ echo | awk "{print \"$PATH\"}"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin$ echo } awk \{print\""$PATH"\"\}} awk {print"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin"}$ 

awk命令还是尽量用单引号引起来,防止shell对其中内容进行解释。就是第一种办法就最好的。

引用命令行定义的变量

最开始的3种方法,有2种是在引号外完成变量定义的,这样就不会对shell进行干扰:

$ echo | awk '{print path}' path="$PATH"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin$ echo | awk -v path="$PATH" '{print path}'                                            /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin$ 

先是用命令行的方式把环境变量赋值给自定义变量,这个操作在引号外。然后再引号里面直接用自定义变量就好了。
如果是在BEGIN块中要这么做,就参考上以小节的做法。

awk 进阶

从free命令同获取当前内存使用数值:

$ free | awk '/^Mem:/ {print $3}'335840$ 

这里用的是正则匹配。不过awk还有其他的一些语法,可以做到更加精确的匹配,

条件限制

限制第一个字段值来匹配:

$ free | awk '$1 == "Mem:" {print $3}'335744$

限制要第几行的数据:

$ free | awk 'NR == 2 {print $3}'        335796$ 

条件语句

awk 也提供了 if, else, while 等这些条件语句,不过似乎用不了那么深,举一个if的例子。
同样是限制第几行,这里通过if语句来判断:

$ free | awk '{if(NR == 2) print $3}'             335740$ 

正则匹配

~是匹配正则表达式的运算符。另外,~!是不匹配正则表达式的运算符。
匹配第一个字段:

$ free | awk '$1 ~ /Mem/ {print $3}'335844$ 

关于正则还有一个内置变量是 IGNORECASE,如果设置为1,可以忽略大小写:

$ free | awk '$1 ~ "mem"  {print $3}' IGNORECASE=1335708$ 

为变量赋值的方法之前讲过了,有好几种方式。

打印九九乘法表

这个很高端的样子,就贴在最后了:

$ seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'1x1=11x2=2   2x2=41x3=3   2x3=6   3x3=91x4=4   2x4=8   3x4=12  4x4=161x5=5   2x5=10  3x5=15  4x5=20  5x5=251x6=6   2x6=12  3x6=18  4x6=24  5x6=30  6x6=361x7=7   2x7=14  3x7=21  4x7=28  5x7=35  6x7=42  7x7=491x8=8   2x8=16  3x8=24  4x8=32  5x8=40  6x8=48  7x8=56  8x8=641x9=9   2x9=18  3x9=27  4x9=36  5x9=45  6x9=54  7x9=63  8x9=72  9x9=81$ 

Zabbix 自定义参数(监控项)

用户自定义参数可以通过Zabbix agent执行非Zabbix原生的agent监控项。只要你有办法能通过命令获取到要监控的指标。

配置文件

可以直接在配置文件 zabbix_agentd.conf 中定义 UserParameter。

### Option: HostnameItem#       Item used for generating Hostname if it is undefined. Ignored if Hostname is defined.#       Does not support UserParameters or aliases.## Mandatory: no# Default:# HostnameItem=system.hostname

Include配置
虽然直接写在这下面就可以了,不过配置文件还有一个Include的配置:

### Option: Include#       You may include individual files or all files in a directory in the configuration file.#       Installing Zabbix will create include directory in /usr/local/etc, unless modified during the compile time.## Mandatory: no# Default:# Include=Include=/etc/zabbix/zabbix_agentd.d/*.conf# Include=/usr/local/etc/zabbix_agentd.userparams.conf# Include=/usr/local/etc/zabbix_agentd.conf.d/# Include=/usr/local/etc/zabbix_agentd.conf.d/*.conf

建议把这些配置分下类,创建独立的 zabbix_agentd.d/*.conf 文件,方便管理。

语法

自定义参数的语法如下:

UserParameter=,

key,就是监控项用的key。必须全局唯一。
命名要求:只能使用字母、数字、下划线、中横杠、点号。即 0-9a-zA-Z_-. 这些字符。

比如下面的文件中定义了3个通过free命令获取值的监控项:

$ cat /etc/zabbix/zabbix_agentd.d/os.confUserParameter=os.memory.total, free -m | awk '$1=="Mem:" {print $2}'UserParameter=os.memory.used, free -m | awk '$1=="Mem:" {print $3}'UserParameter=os.memory.free, free -m | awk '$1=="Mem:" {print $4}'

具体一步步如何实现的,参考下一小节。

调试步骤

自定义参数,一步步实现的操作过程。

第一步:写一个命令或脚本
能够成功的在命令行中把值打印出来:

$ free -m | awk '$1=="Mem:" {print $3}' 569$ 

由于zabbix是使用zabbix账号执行的,有些命令有可能zabbix无权限。所以可以加上sudo指定zabbix用户执行再验证一下:

$ sudo -u zabbix free -m | awk '$1=="Mem:" {print $3}'571$ 

第二步:添加到配置文件中

UserParameter=os.memory.used, free -m | awk '$1=="Mem:" {print $3}'

第三步:测试key
使用 zabbix_agentd 并且用 -t 选项指定key来进行测试:

$ zabbix_agentd -t os.memory.usedos.memory.used                                [t|571]$ 

测试成功说明写的没问题

第四步:重启agent
要重启agent才能使新的配置文件生效:

$ systemctl restart zabbix-agent$

之后就可以去Web添加监控项了。

带参数的用法

可以为key设置参数,这样一个设置可以应对多个监控项。
语法如下:

UserParameter=key[*],command

这里的星号表示可以带任意数量的参数,并且似乎也只有这一种用法,没有指定参数数量的写法。

在command中使用参数
在command中使用位置引用$1......$9,来引用key中相应的参数。
另外$0表示命令本身。

关于$符号
由于$1.....$9有了特殊的意义,在awk中的$1也会被zabbix先替换掉。这时应该使用$$1。
zabbix仅仅只替换位置参数,对于单独的$符号或者其他组合(比如$NF),zabbix不会处理。
zabbix仅仅只在使用了key[*],这样指定了key是带参数的时候才会进行位置参数替换的处理。所以之前的示例使用$1没有问题。

修改为带参数的key
现在把之前的示例改成一种更灵活的设置方式:

UserParameter=os.free[*], free -m | awk '$$1~NAME {print $$(COLUMN+1)}' IGNORECASE=1 NAME="$1" COLUMN=$2

测试效果如下:

$ zabbix_agentd -t os.free[mem,2]os.free[mem,2]                                [t|570]$ 

示例

一些自定义参数的示例:

UserParameter=Nginx.active[*], /usr/bin/curl -s "http://$1:$2/status" | awk '/^Active/ {print $NF}'UserParameter=Nginx.reading[*], /usr/bin/curl -s "http://$1:$2/status" | grep 'Reading' | cut -d" " -f2UserParameter=Nginx.writing[*], /usr/bin/curl -s "http://$1:$2/status" | grep 'Writing' | cut -d" " -f4UserParameter=Nginx.waiting[*], /usr/bin/curl -s "http://$1:$2/status" | grep 'Waiting' | cut -d" " -f6UserParameter=Nginx.accepted[*], /usr/bin/curl -s "http://$1:$2/status" | awk '/^([ \t]+[0-9]+){3}/ {print $$1}'UserParameter=Nginx.handled[*], /usr/bin/curl -s "http://$1:$2/status" | awk '/^([ \t]+[0-9]+){3}/ {print $$2}'UserParameter=Nginx.requests[*], /usr/bin/curl -s "http://$1:$2/status" | awk '/^([ \t]+[0-9]+){3}/ {print $$3}'UserParameter=os.free[*], free | awk '$$1~NAME {print $$(COLUMN+1)}' IGNORECASE=1 NAME="$1" COLUMN=$2UserParameter=Mysql.dml[*] -h$1 -u$2 -p$3 -e 'SHOW GLOBAL STATUS' | awk '/^Com_$4\>/ {print $$2}'

正则表达式 词尾锚定
在调试mysql的时候,遇到一些问题。正则表达式匹配不够精确,有多个值:

$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_select/ {print $0}'       Com_select      67679$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_update/ {print $0}'       Com_update      1098Com_update_multi        0$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_delete/ {print $0}'       Com_delete      678Com_delete_multi        0$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_insert/ {print $0}'       Com_insert      38494Com_insert_select       0$ 

这里是加了词尾锚定:

$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_delete\>/ {print $0}'  Com_delete      708$ 

词尾锚定是 \>,顺便词首就是 \<。

其实也没那么复杂,还有很多办法:

$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_delete[" "\t]/ {print $0}'   Com_delete      712$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_delete[^_]/ {print $0}'      Com_delete      715$ mysql -e 'SHOW GLOBAL STATUS' | awk '$1 == "Com_delete" {print $0}' Com_delete      717$

system.run

这个是zabbix内置key,也能够实现同样的功能,那么到底用哪个好?

内置key system.run
这是一个内置key:

system.run[command,]

在主机上指定的命令的执行。返回命令执行结果的文本值。如果指定NOWAIT的模式,这将返回执行命令的结果1。

  • command: 命令
  • mode: wait (默认值, 执行超时时间), nowait (不等待)
  • 最大可用返回512KB数据,包含空白数据。命令输出数据必须是文本

默认agent不支持,安全隐患还是很大的。需要agent端开启RemoteCommand,允许远程执行命令。

优劣比较
通过这个也能实现自定义监控功能,而且不用去agent上定义UserParameter。直接在web就能完成全部操作。这个可能是好处。
用UserParameter,如果agent多,需要每一台agent上都去设置UserParameter,这就很烦。不过还有自动化运维工具可以解决批量更新、操作文件的问题。

变量 命令 分隔符 引号 参数 文件 输出 就是 字符 正则 处理 方法 表达式 监控 配置 文本 内容 多个 环境 问题 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 小米移动互联网科技园 天津市摩尔互联网科技 山东特亿宝互联网科技有限公司 圆梦互联网科技 互联网科技与金融科技 科技制造互联网公司 深圳互联网络科技有限公司 上海致骋互联网科技 随着互联网科技发展 汽车互联网创新科技园 昊天互联网科技有限公司 赶超互联网科技 微信多开 成都互联网科技公司工作好不好 深圳艾果互联网通讯科技 工业互联网等科技实现智能化赋能 上海创道互联网络科技有限公司 上海乐五互联网科技有限公司 世界互联网科技成果奖 银川新华互联网科技学校原画 互联网金融金融科技 上海挚品互联网科技有限公司 互联网公司 科技公司 互联网科技公司怎么样 互联网女皇 腾讯科技 科技大学互联网专业好不好 银川新华互联网科技学校联系电话 燊美集互联网科技有限公司 华夏恒生互联网科技013171 2021互联网科技公司排名 上海旺乐尼互联网科技有限公司
0