awk命令

-- TOC --

awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

awk命令的功能可以写一本书,这里只是做点个人总结。

awk可以接收stdin的输入,也可以读文件:

$ echo sth | awk ...
$ awk ... <filename>

用print提取field

在命令行的世界中,不同工具之间的数据交换基本都是通过管道,左边命令的输出stdout是右边命令的输入stdin,获取命令输出的方式,|或者$(cmd)赋值,awk的print输出也就是输出到stdout的意思。

-F指定field seperator,默认是空格。

$ uname -a | awk '{print $0}'
Linux K 5.14.14 #1 SMP Tue Oct 26 10:20:53 CST 2021 x86_64 x86_64 x86_64 GNU/Linux
$ uname -a | awk '{print $1}'
Linux
$ echo '123a567a789a' | awk -Fa '{print $2}'
567

$0表示整行,field从$1开始编号!可以借助此功能,对列进行重新排列:

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

""括起来的部分是raw格式输出,在其中使用awk分割出来的field,要用\转义。

单引号时,将单引号内所有内容原样传递给awk去解析;双引号时,如果不用\转义,bash就会将$1展开。

下面是一个带条件提取field的case:

$ cat /proc/devices | grep mem
  1 mem
$ awk '$2=="mem" {print $1}' /proc/devices
1
$ awk "\$2==\"${mem}\" {print \$1}" /proc/devices  # ??
1

另一种写法如下,先用关键词匹配某一行,然后再输出field:

$ awk '/mem/{print $1}' /proc/devices
1
$ awk '/em/{print $1}' /proc/devices
1

提取最后一列的技巧:NF$NF

$ uname -a | awk '{print NF" "$NF}'
15 GNU/Linux

NF是awk的一个内置变量,值为列数,$NF自然就是最后一列了!

NR也是内置变量,表示当前行数,从1开始!

显示当前分区剩余的空间:

$ df -k . | awk 'NR==2 {printf("%d%%, %d Bytes \n", $5, $4)}'
70%, 11886388 Bytes 

用printf格式化输出

print有的时候还不够,更复杂的控制输出的方式,还是printf。

$ echo '1 2 3' | awk '{printf("%d - %0.2f - %d\n",$1,$2,$3)}'
1 - 2.00 - 3
$ df -h | awk '{printf("%18s  %-4s\n", $1,$5)}'
$ df -h | awk '{printf "%18s  %-4s\n",$1,$5}'  # 没有括号也OK!
        Filesystem  Use%
              udev  0%  
             tmpfs  1%  
         /dev/sda5  69% 
             tmpfs  0%  
             tmpfs  1%  
             tmpfs  0%  
        /dev/loop0  100%
        /dev/loop1  100%
        /dev/loop2  100%
        /dev/loop3  100%
        /dev/loop4  100%
        /dev/loop5  100%
        /dev/loop6  100%
        /dev/loop8  100%
        /dev/loop9  100%
       /dev/loop10  100%
       /dev/loop11  100%
       /dev/loop12  100%
        /dev/loop7  100%
         /dev/sda1  1%  
             tmpfs  1%  

看到一种用法,用awk的printf输出一个字符串,然后用eval执行此字符串!举个例子:

$ cmd=$(echo 1 | awk '{printf("sleep %s", $1)}')
$ eval $cmd  # 没有eval也OK

来自一个shell脚本文件:

if [ -f $NETCFG_FILE ] ; then
  echo "$NETCFG_FILE exist"
  argv=`awk -F ':' '{printf("dhcpoff=%s;ip=%s;mask=%s;gw=%s;dns1=%s;dns2=%s;",$1,$2,$3,$4,$5,$6)}' $NETCFG_FILE`
  eval $argv
fi

$argv实际上就是一行赋值命令,生成了一组shell变量,这条命令的组成来自awk和printf。

本文链接:https://cs.pynote.net/sf/linux/shell/202112017/

-- EOF --

-- MORE --