awk命令使用实践

Author Avatar
呃哦 5月 26, 2018

awk 算是个古老的命令了,上古神器。现在已经很少看到使用,可能我周围没什么运维的朋友吧,至少我周围没见到用这个命令,不过也确实有点不好学,所以记录下。

语法

awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)

常用参数

-F fs   fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:,多个分隔符(例如:和制表符)为awk -F '[:\t]' 或者 '[:|\t]' 或者 '[":"|"\t"]',表示没发现区别
-v var=value   赋值一个用户定义变量,将外部变量传递给awk
-f scripfile  从脚本文件中读取awk命令
-m[fr] val   对val值设置内在限制,-mf选项限制分配给val的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。

摘要介绍

日常Linux流程,遇见不会的命令,先 man command,所以 awk 的文档介绍是这么写的 mawk - pattern scanning and text processing language 用我的菜鸡英语翻译就是:模式扫描和文本处理语言。

是的,awk 并不是一个二进制程序命令,而是一门脚本语言,主要用来匹配处理文本,就像 bash 一样,同理 bash 有多个解释器: zsh, fish 等一样,awk 也有,例如我 man awk 看到的是 mawk,我的是Ubuntu16.04系统。因此既然 awk 是语言,mawk 是解释器而已,所以我们的操作可以是将 awk 代码直接在终端中使用,就像bashecho 'hello world'一样,也可以写成一个脚本,通过 awk -f filename 一样执行。

awk的处理方式是从输入流从逐行读取文本,然后进行 模式-动作 流程,即

pattern    { action }

小例子:

$ ls | awk '1 < 2 {print "废话"}'
废话

这里的 1 < 2 就是 pattern 了,而 action 要包裹在 {} 里面。

使用实践-剪切获取ifconfig中的ip地址

文本如下

$ ifconfig wlp8s0 
wlp8s0    Link encap:Ethernet  HWaddr 5c:93:a2:76:e5:c1  
          inet addr:192.168.123.111  Bcast:192.168.123.255  Mask:255.255.255.0
          inet6 addr: fe80::44cd:39be:6aea:d61a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:698409 errors:0 dropped:0 overruns:0 frame:0
          TX packets:353304 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:949454893 (949.4 MB)  TX bytes:38261895 (38.2 MB)

简单的剪切思路,先通过正则表达式获取ip地址那一行,awk默认的分隔符是空格,所以很明显我们的ip地址在第二列。

$ ifconfig wlp8s0 | awk '$0 ~ /inet addr/ {printf $2}'
addr:192.168.123.111

解释如下:

  • $0 表示当前读取的整行,上面文本有
  • ~ 表示开启正则匹配
  • /inet addr/ 正则匹配内容,表达式要写在//中间。即/pattern/
  • {print $2} 表示匹配成功的话打印当前行的第二列。$2是内键变量,参考下面附录

接下来就很明显了,针对剪切出来的 addr:192.168.123.111 我们只需要以 : 为分隔符打印第二列就可以了。命令如下:

ifconfig wlp8s0 | awk '$0 ~ /inet addr/ {print $2}' | awk -F: '{print $2}'

小提示:

  • 当把awk代码直接放在终端中使用时,建议使用 ' 而不是 " 包括起来,因为 " 可能会被bash解析了变量$0之类
  • printprintf的区别在于printf可以格式化输出,但是print自带换行

printf 示例如下

# 打印行号
$ ls / | awk '{printf "%d  %s\n", NR, $0}' 
行号:1  bin
行号:2  boot
行号:3  cdrom
行号:4  dev
行号:5  etc
行号:6  home
行号:7  initrd.img
行号:8  initrd.img.old
行号:9  lib
行号:10  lib64
行号:11  lost+found
行号:12  media
行号:13  mnt
行号:14  opt
行号:15  proc
行号:16  root
行号:17  run
行号:18  sbin
行号:19  snap
行号:20  srv
行号:21  sys
行号:22  tmp
行号:23  usr
行号:24  var
行号:25  vmlinuz
行号:26  vmlinuz.old

事实上这用 nl 命令即可了。

ls / | nl

附录-内键变量

变量 描述
\$n 当前记录的第n个字段,字段间由FS分隔
\$0 完整的输入记录
ARGC 命令行参数的数目
ARGIND 命令行中当前文件的位置(从0开始算)
ARGV 包含命令行参数的数组
CONVFMT 数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组
ERRNO 最后一个系统错误的描述
FIELDWIDTHS 字段宽度列表(用空格键分隔)
FILENAME 当前文件名
FNR 各文件分别计数的行号
FS 字段分隔符(默认是任何空格)
IGNORECASE 如果为真,则进行忽略大小写的匹配
NF 一条记录的字段的数目
NR 已经读出的记录数,就是行号,从1开始
OFMT 数字的输出格式(默认值是%.6g)
OFS 输出记录分隔符(输出换行符),输出时用指定的符号代替换行符
ORS 输出记录分隔符(默认值是一个换行符)
RLENGTH 由match函数所匹配的字符串的长度
RS 记录分隔符(默认是一个换行符)
RSTART 由match函数所匹配的字符串的第一个位置
SUBSEP 数组下标分隔符(默认值是/034)