本文已迁移至 segmentfault
背景
虽然对awk早有耳闻,据说是个很强大的工具,但一直没机会去了解和使用,最近碰到一个需求,用awk轻松解决,才真正一窥它的厉害。
需求是这样的,应用每次升级都会构建一个新的容器镜像,时间久了,服务器上会积累很多历史镜像,而且因为镜像本身比较大,磁盘消耗特别快,因此需要写一个脚本定期保留最近N个镜像,清除其余历史镜像。
初步列了以下方案
- 用java实现(擅长java~~),通过重定向将
docker images
的输出传给java,java处理完后执行系统命令清除镜像。 - 用python处理。
- 用bash处理。
java方案虽然可行,但想想代码量就不小,既要处理字符串,又要调用系统命令,而且把java拿来做这种事,总感觉怪怪的。pass。
python不熟,又懒得去学,学完之后估计很长时间不会再用,下次用的时候还得去学,性价比不高,pass。
最理想的就是bash,正宗的脚本语言,但是否有足够能力处理字符串,需要做技术调研,这时想到了awk。
awk介绍
awk不只是linux的一个工具,由于awk脚本具有编程语言三要素,顺序,循环,判断,awk还是一门编程语言,主要用于数据处理和数据计算。awk对文本进行行扫描,以行为单位进行处理,按照以下逻辑进行处理
pattern { action }
pattern { action }
....
其中pattern
是匹配条件,action
是pattern
匹配后执行的动作。
上面提到awk对文本以行为单位进行处理,读取行后,awk默认以空格为切割符对行进行切割(split)操作并按顺序存放在变量$1$2,$3....
中,其中$0
表示完整行数据,这些变量可以用于pattern
和action
。
举个例子,比如用ps -ef
列出目前系统进程
zhengjianfengdeMacBook-Pro:shell asan$ ps -ef
UID PID PPID C STIME TTY TIME CMD
0 1 0 0 17 418 ?? 21:55.75 /sbin/launchd
0 37 1 0 17 418 ?? 1:50.35 /usr/libexec/UserEventAgent (System)
0 38 1 0 17 418 ?? 0:37.59 /usr/sbin/syslogd
0 40 1 0 17 418 ?? 0:14.22 /System/Library/PrivateFrameworks/Uninstall.framework/Resources/uninstalld
0 41 1 0 17 418 ?? 0:09.93 /usr/libexec/kextd
0 42 1 0 17 418 ?? 4:15.97 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd
0 44 1 0 17 418 ?? 0:36.08 /opt/cisco/anyconnect/bin/vpnagentd -execv_instance
55 48 1 0 17 418 ?? 0:03.84 /System/Library/CoreServices/appleeventsd --server
如果我们想要打印出第二列PID
的值可以这么写
zhengjianfengdeMacBook-Pro:shell asan$ ps -ef|awk '{print $2}'
PID
1
37
38
40
41
42
44
48
如果想要打印PID
大于40值可以这么写
zhengjianfengdeMacBook-Pro:shell asan$ ps -ef|awk '$2>40 {print $2}'
PID
41
42
44
48
本文不是awk入门教程,如果想了解更多awk基本用法,可以阅读以下两篇文章:
http://awk.readthedocs.io/en/latest/chapter-one.html
http://www.runoob.com/linux/linux-comm-awk.html
实现
获取目前系统镜像列表可以通过命令docker images
获取,输出格式如下
[root@definesys /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/xxxxx-authority-ms v1.0.3 02da1a24ac2c 4 days ago 692 MB
docker.io/xxxxx-authority-ms v1.0.2 4a36396f0fea 4 days ago 692 MB
docker.io/xxxxx-uc-user-ms v.1.1.11 6f0db317c7a9 5 days ago 695 MB
docker.io/xxxxx-uc-user-ms v.1.1.10 26983c00bb73 6 days ago 695 MB
docker.io/xxxxx-uc-user-ms v1.1.9 f21d59255405 7 days ago 695 MB
- 获取镜像列表
第一步就是要先获取系统的镜像列表,也就是对REPOSITORY
进行去重操作,awk去重有个经典的写法。
awk '!a[$0]++ {print $0}'
声明一个hash数组a
并且以$0
即行数据为key,第一次a[$0]
值是未定义的(undef)取反后为true
输出,++对a[$0]
进行赋值,赋值后a[$0]
值为1
,第二次a[$0]
为1,取反后为0,0++还是0不输出。
对镜像列表进行去重操作可以以镜像名称(REPOSITORY)为key进行去重
docker images|awk '!a[$1]++ {print $1}'
- 获取历史镜像
要保留最近N个镜像清除历史镜像,其实就是保留数据前N行,awk有个内置变量NR
保存当前处理行编号。结合第一步获取的镜像名称可以先grep出指定镜像列表,再用awk进行筛选.
docker images|grep $image|awk 'NR > 4 {printf "%s:%s:%s\n",$1,$2,$3}'
- 完整脚本
#!/bin/sh
#create by jianfeng.zheng
#group docker images
for image in `docker images|awk '!a[$1]++ {print $1}'`
do
for m in `docker images|grep $image|awk 'NR > 4 {printf "%s:%s:%s\n",$1,$2,$3}'`
do
id=`echo $m|awk -F ":" '{print $3}'`
##do delete
echo 'delete docker== > '$m
done
done
结语
awk还有很多功能待挖掘,但可以肯定的是,以后Linux上数据处理的工作,首选方案就是awk。