溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

awk命令與Zabbix自定義參數(shù)的介紹

發(fā)布時間:2020-05-23 15:40:24 來源:億速云 閱讀:321 作者:鴿子 欄目:系統(tǒng)運維

awk 命令

awk是一種處理文本文件的語言,是一個強大的文本分析公具。
awk處理文本和數(shù)據(jù)的方式:逐行讀入文本,尋找匹配特定模式的行,然后進行操作。

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

功能很強大,所以有很多用處。這里我主要關注下面這樣的場景:
逐行讀入文本,按規(guī)則匹配特定的行,以空格為默認分隔符將每行切片,輸出其中特定的某個切片(切開的部分可以進行各種分析處理,這里就是要輸出其中以段):

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

這種方法很適合用來做zabbix的自定義key的監(jiān)控。比如從free命令中,提取出內存的使用量:

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

grep命令  
同樣的效果,也可以通過grep命令來把需要的行過濾出來,然后還得借助cut命令來進行列切割。  
但是使用awk的話就一步搞定了。

內置變量

內置變量先列出來,后面會用到其中一些。
awk內置變量:

  • ARGC: 命令行參數(shù)個數(shù)
  • ARGV: 命令行參數(shù)排列
  • ENVIRON: 支持隊列中系統(tǒng)環(huán)境變量的使用
  • FILENAME: awk瀏覽的文件名
  • FNR: 瀏覽文件的記錄數(shù)
  • FS: 設置輸入域分隔符,等價于命令行 -F選項
  • IGNORECASE: 如果把這個變量設為1,則正則表達式忽略大小寫
  • NF: 瀏覽記錄的域的個數(shù)
  • NR: 已讀的記錄數(shù)
  • OFS: 輸出域分隔符
  • ORS: 輸出記錄分隔符
  • RS: 控制記錄分隔符
  • $0: 變量是指整條記錄
  • $1: 表示當前行的第一個域,$2表示當前行的第二個域,......以此類推
  • $NF: 既然 NF是列的總數(shù),那么$NF就是最后一列的值
  • $NF-1: 這個是倒數(shù)第二列,其他以此類推

上面這些變量,有些是直接來使用的。比如$1,$NF,后面的例子中會用到,也比較好理解。  
還有些是用來改變awk行為的,需要對變量進行設置,這個需要會為變量賦值,有多種方式可以實現(xiàn)。
比如FS,是用來指定分隔符的,默認的分隔符是空白符,但是可以指定。這就需要自己定義FS的值。不過分隔符還提供了一個 -F 選項來定義。所以也可以在命令行選項中設置。
但是其他一些變量需要指定,但又沒有提供別的方法的話,就只能用過為變量賦值來實現(xiàn)了。
分隔符和為變量賦值的方式在后面會展開,為變量賦值參考自定義變量的內容。

分隔符

默認awk是以空白符來做分隔的。使用 -F 選項可以自定義分隔符:

$ grep -e "^root" /etc/passwd
root:x:0:0:root:/root:/bin/bash
$ awk -F: '/^root/ {print $1,$NF}' /etc/passwd
root /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
$ 

過濾連續(xù)的分隔符  
-F 選項也是支持正則表達式的,中括號就是正則表達式字符集合的意思。但是如果這時遇到連續(xù)的分隔符,就會有問題。下面使用逗號和空格作為分隔符,并且每次都連續(xù)出現(xiàn):

$ 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
$ 

默認分隔符  
默認就是空白符作為分隔符,并且能夠識別連續(xù)的空白符。默認分隔符就是下面的這個正則表達式:

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

看上面的內置變量,F(xiàn)S和-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/bash
filename:/etc/passwd,linenumber:2,columns:7,linecontent:bin:x:1:1:bin:/bin:/sbin/nologin
filename:/etc/passwd,linenumber:3,columns:7,linecontent:daemon:x:2:2:daemon:/sbin:/sbin/nologin

對比下用printf格式化輸出后的效果:

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

BEGIN 和 END 模塊

通常,對于每個輸入行,awk 都會執(zhí)行一次腳本代碼塊。  
有時,需要在 awk 開始處理輸入文件中的文本之前執(zhí)行初始化代碼。這就需要定義一個 BEGIN 塊。
另外,還有一個 END 塊,用于執(zhí)行最終計算或打印應該出現(xiàn)在輸出流結尾的摘要信息。

在BEGIN塊中定義內置變量  
這里在BEGIN塊中定義了兩個內置變量:

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

FS是分隔符,OFS是輸出字段分隔符。在之前的例子中,不用BEGIN塊也是能實現(xiàn)這個效果的。  
這里修改的是一個內置變量,但是方法是針對變量的,包括自定義變量。具體參考下一章“awk自定義變量”。

這里主要挑BEGIN塊舉例用法。END塊可以實現(xiàn)計算統(tǒng)計輸出的功能,暫時用不上,略過。

awk自定義變量

除了內置變量,也可以定義自定義變量并使用。這部分內容對于靈活的配置非常有用,而且如果自己寫,也會遇到一些坑。

定義在后面

這里變量的賦值在發(fā)生在BEGIN塊執(zhí)行之后的
直接寫在后面:

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

這種用法在BEGIN塊中是識別不了變量的。BEGIN塊的執(zhí)行在這些變量定義之前。不過還有其他的方法可以用。
另外,這里使用管道作為標準輸入。如果是從文件輸入的話,文件路徑在寫最后。

在BEGIN塊中定義

這里變量額賦值是在BEGIN塊執(zhí)行的時候
在BEGIN塊中可以對內置變量賦值,同樣的也可以為自定義變量賦值

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

使用 -v 選項

這里變量的賦值是在BEGIN塊執(zhí)行之前
這個方法在發(fā)生在BEGIN塊執(zhí)行之前的:

$ 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 v2
ACTION: VALUE1 VALUE2
$  

先是 -v 進行賦值,然后BEGIN塊執(zhí)行。之后是最后的變量賦值,如果有同名的就替換值,之后再逐行執(zhí)行。打印出來的就是之后改變的值。

打印環(huán)境變量

最好的方法在后一小節(jié)。這里的方法也是可行的,但是可讀性不好。
寫這段是為了理解一下命令參數(shù)解析的過程,以及一些特殊情況的處理。
要直接打印環(huán)境變量是這樣的:

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

這里的顯示不明顯,兩邊都是一對雙引號套一個單引號,看圖

awk命令與Zabbix自定義參數(shù)的介紹

解釋說明  
先用一個簡單點的環(huán)境變量來舉例:

$ echo $USER
root
$ 

這個沒有什么空格換特殊字符,這樣可以去掉最里面的一對雙引號:

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

這里成對出現(xiàn)了2對單引號,所以就被分成了這樣兩個部分:awk  '{print'"}'。awk對2個單引號內的命令起作用。
剩下的就是  $USER 了,這個最早就被 shell 給處理替換了。  
在變量本身被shell處理完之后,如果有空格之類的,有會被認為不是一個部分。這里就再用雙引號把環(huán)境變量的值包起來,將值作為整體的一個域。

我的理解  
最外層的引號是用來界定字符邊界的,但是只要是連續(xù)的就被系統(tǒng)認為是一串(一個域)??梢杂枚鄬σ柊讯鄠€字符串引起來,但是每對引號之間不要出現(xiàn)分隔符。這樣,最后解析交給命令處理的還是一個整體的字符串(一個域)。  
下面是用echo命令的演示:

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

加上for循環(huán)再演示一次:

$ HELLO='Hello World !'
$ for i in $HELLO; do echo $i;done
Hello
World
!
$ for i in 'BEFOR'$HELLO'AFTER'; do echo $i;done
BEFORHello
World
!AFTER
$ for i in 'BEFOR'"$HELLO"'AFTER'; do echo $i;done
BEFORHello World !AFTER
$ 

最外層的引號僅僅是界定邊界的,用多對引號但是所有內容都相連,也被認為是一個域。

雖然有多對引號,但是所有內容都是相連的,沒有分隔符,最后交給命令處理的還是一個域。
這樣做的好處就是,用了單引號,但是把需要shell解析的部分放到了單引號的外面,這樣shell還是可以正常解析。
為了保證環(huán)境變量解析完之后依然是一個域,需要用雙引號引起來。
再來就是awk中接著print的雙引號了。awk中的引號不是界定邊界的而是區(qū)分是變量還是字符串的。沒有雙引號的話表示這個內容是變量,用雙引號引起來表示里面的內容是字符串,直接打印。

其他寫法  
下面兩種寫法也能實現(xiàn)同樣的效果,幫助理解吧:

$ 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
$ 

先是用命令行的方式把環(huán)境變量賦值給自定義變量,這個操作在引號外。然后再引號里面直接用自定義變量就好了。  
如果是在BEGIN塊中要這么做,就參考上以小節(jié)的做法。

awk 進階

從free命令同獲取當前內存使用數(shù)值:

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

這里用的是正則匹配。不過awk還有其他的一些語法,可以做到更加精確的匹配,

條件限制

限制第一個字段值來匹配:

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

限制要第幾行的數(shù)據(jù):

$ 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=1
335708
$ 

為變量賦值的方法之前講過了,有好幾種方式。

打印九九乘法表

這個很高端的樣子,就貼在最后了:

$ 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=1
1x2=2   2x2=4
1x3=3   2x3=6   3x3=9
1x4=4   2x4=8   3x4=12  4x4=16
1x5=5   2x5=10  3x5=15  4x5=20  5x5=25
1x6=6   2x6=12  3x6=18  4x6=24  5x6=30  6x6=36
1x7=7   2x7=14  3x7=21  4x7=28  5x7=35  6x7=42  7x7=49
1x8=8   2x8=16  3x8=24  4x8=32  5x8=40  6x8=48  7x8=56  8x8=64
1x9=9   2x9=18  3x9=27  4x9=36  5x9=45  6x9=54  7x9=63  8x9=72  9x9=81
$ 

Zabbix 自定義參數(shù)(監(jiān)控項)

用戶自定義參數(shù)可以通過Zabbix agent執(zhí)行非Zabbix原生的agent監(jiān)控項。只要你有辦法能通過命令獲取到要監(jiān)控的指標。

配置文件

可以直接在配置文件 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

建議把這些配置分下類,創(chuàng)建獨立的 zabbix_agentd.d/*.conf 文件,方便管理。

語法

自定義參數(shù)的語法如下:

UserParameter=<key>,<command>

key,就是監(jiān)控項用的key。必須全局唯一。
命名要求:只能使用字母、數(shù)字、下劃線、中橫杠、點號。即 0-9a-zA-Z_-. 這些字符。

比如下面的文件中定義了3個通過free命令獲取值的監(jiān)控項:

$ cat /etc/zabbix/zabbix_agentd.d/os.conf
UserParameter=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}'

具體一步步如何實現(xiàn)的,參考下一小節(jié)。

調試步驟

自定義參數(shù),一步步實現(xiàn)的操作過程。

第一步:寫一個命令或腳本
能夠成功的在命令行中把值打印出來:

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

由于zabbix是使用zabbix賬號執(zhí)行的,有些命令有可能zabbix無權限。所以可以加上sudo指定zabbix用戶執(zhí)行再驗證一下:

$ 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.used
os.memory.used                                [t|571]
$ 

測試成功說明寫的沒問題

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

$ systemctl restart zabbix-agent
$

之后就可以去Web添加監(jiān)控項了。

帶參數(shù)的用法

可以為key設置參數(shù),這樣一個設置可以應對多個監(jiān)控項。
語法如下:

UserParameter=key[*],command

這里的星號表示可以帶任意數(shù)量的參數(shù),并且似乎也只有這一種用法,沒有指定參數(shù)數(shù)量的寫法。

在command中使用參數(shù)
在command中使用位置引用$1......$9,來引用key中相應的參數(shù)。
另外$0表示命令本身。

關于$符號
由于$1.....$9有了特殊的意義,在awk中的$1也會被zabbix先替換掉。這時應該使用$$1。
zabbix僅僅只替換位置參數(shù),對于單獨的$符號或者其他組合(比如$NF),zabbix不會處理。
zabbix僅僅只在使用了key[*],這樣指定了key是帶參數(shù)的時候才會進行位置參數(shù)替換的處理。所以之前的示例使用$1沒有問題。

修改為帶參數(shù)的key
現(xiàn)在把之前的示例改成一種更靈活的設置方式:

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]
$ 

示例

一些自定義參數(shù)的示例:

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" " -f2
UserParameter=Nginx.writing[*], /usr/bin/curl -s "http://$1:$2/status" | grep 'Writing' | cut -d" " -f4
UserParameter=Nginx.waiting[*], /usr/bin/curl -s "http://$1:$2/status" | grep 'Waiting' | cut -d" " -f6
UserParameter=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=$2

UserParameter=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      1098
Com_update_multi        0
$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_delete/ {print $0}'       
Com_delete      678
Com_delete_multi        0
$ mysql -e 'SHOW GLOBAL STATUS' | awk '/^Com_insert/ {print $0}'       
Com_insert      38494
Com_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,也能夠實現(xiàn)同樣的功能,那么到底用哪個好?

內置key system.run
這是一個內置key:

system.run[command,<mode>]

在主機上指定的命令的執(zhí)行。返回命令執(zhí)行結果的文本值。如果指定NOWAIT的模式,這將返回執(zhí)行命令的結果1。

  • command: 命令
  • mode: wait (默認值, 執(zhí)行超時時間), nowait (不等待)
  • 最大可用返回512KB數(shù)據(jù),包含空白數(shù)據(jù)。命令輸出數(shù)據(jù)必須是文本

默認agent不支持,安全隱患還是很大的。需要agent端開啟RemoteCommand,允許遠程執(zhí)行命令。

優(yōu)劣比較
通過這個也能實現(xiàn)自定義監(jiān)控功能,而且不用去agent上定義UserParameter。直接在web就能完成全部操作。這個可能是好處。
用UserParameter,如果agent多,需要每一臺agent上都去設置UserParameter,這就很煩。不過還有自動化運維工具可以解決批量更新、操作文件的問題。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI