Linux命令三剑客的一顿操作
今天在写一个脚本的时候,遇到了很多字符过滤的问题,感觉还是有些技术含量的,这里记录下来,以便后续参阅。
我们都知道Linux常用命令有很多,我们把grep、sed和awk称之为Linux命令的"三剑客",能叫三剑客,必有过人之处,这三个命令加起来,可以实现很多我们想要的功能,在文本处理领域简直是上天入地,无所不能。三剑客,顾名思义,就是三个剑客,像下面这样:
你可以把他理解成海、陆、空,亦或天、地、人,也可以理解为剑圣、剑魔、剑贪,算了,编不下去了,开始正题吧,哈哈哈~(有点儿像沙雕网友)
01
问题描述
实际上我是在处理这样一个问题:在mysql的一个binlog日志文件中,找到从库的复制偏移量,也就是master_binlog_num和master_binlog_pos,找到了这两个值之后,需要确定这个master_binlog_pos生成的时间是什么时候。
上面的描述可能不是特别清楚,我们先看下一个binlog里面的内容吧:
# at 181
#190428 11:47:54 server id 10226 end_log_pos 180578 Query thread_id=4631538 exec_time=0 error_code=0
use shop_gp/*!*/;
SET TIMESTAMP=1556423274/*!*/;
SET @@session.time_zone='SYSTEM'/*!*/;
如上所述,位置181的生成时间就是10年04月28日,11点47分54秒,要实现这个目标,我们需要把这个at 181下面的一行数据找到,然后进行文本过滤,最后生成我们想要的目标:2019-04-28 11:47:54。
那么具体怎么做呢,来看实际操作。
02
实际操作
1.找到181所在的位置:
[root innodblog]# mysqlbinlog mysqlbin.013613|grep '# at 181'
# at 181
上面这个结果比较好找,我们直接使用mysqlbinlog来查看这个二进制日志文件即可,然后再后面通过grep来过滤出来我们想要的位置,如上述代码所示。
2.当我们找到这个固定位置之后,需要找到这个位置的下一行,这里我们可以使用grep里面的-A参数,关于-A参数,我们简单进行说明:
grep -A n
A是after的意思,显示过滤到的行数,后面可以跟数字,除了显示符合范本样式的那一列之外,并显示该行之后n行的内容。
来看示例:
[root innodblog]# mysqlbinlog mysqlbin.013613|grep -A 1 '# at 181'
# at 181
#190428 11:47:54 server id 10226 end_log_pos 180578 Query thread_id=4631538 exec_time=0 error_code=0
[root innodblog]# mysqlbinlog mysqlbin.013613|grep -A 2 '# at 181'
# at 181
#190428 11:47:54 server id 10226 end_log_pos 180578 Query thread_id=4631538 exec_time=0 error_code=0
use shop_gp/*!*/;
[root innodblog]# mysqlbinlog mysqlbin.013613|grep -A 3 '# at 181'
#190428 11:47:54 server id 10226 end_log_pos 180578 Query thread_id=4631538 exec_time=0 error_code=0
use jj_shop_gp/*!*/;
SET TIMESTAMP=1556423274/*!*/;
这里多说一句,-B参数是before的意思,和-A操作差不多,大家可以试一下。
3.我们使用grep -A 1的方法取到下一行的值,当我们拿到这个值的时候,接下来要做一个tail的操作,这个tail的作用是指获取带有时间的那一行值,而把我们过滤用的at 181那一行去除掉:
[root innodblog]# mysqlbinlog mysqlbin.013613|grep -A 1 '# at 181'|tail -1
#190428 11:47:54 server id 10226 end_log_pos 180578 Query thread_id=4631538 exec_time=0 error_code=0
这样,我们就拿到了含有时间的那一行值,对这行值我们需要再次进行过滤。
4.这里,awk命令就派上用场了,关于这个awk命令,之前的文章里面有讲过,这里我们不在进行赘述,有兴趣的同学可以翻看前面的文章,我们使用awk命令,以server id为分割条件,将语句进行分割,然后取前半部分,写出来就是:
[root@tk-dba-dr-mysql226 innodblog]# mysqlbinlog mysqlbin.013613|grep -A 1 '# at 181'|
tail -1|awk -F'server id' '{print $1}'
#190428 11:47:54
可以看到,第3行的结果已经距离我们的目标很接近了。
5.现在我们的结果是#190428 11:47:54 ,我们想要的结果是:2019-04-28 11:47:54,首先我们需要将#换成20,也就是将结果转化成20190428 11:47:54,操作如下:
[root innodblog]# mysqlbinlog mysqlbin.013613|grep -A 1 '# at 181'
|tail -1|awk -F'server id' '{print $1}'|sed s/^#/20/g
20190428 11:47:54
说说最后的sed命令,这个sed命令用来替换行,它的语法是sed s/原来的字符串/替换后的字符串/g
我们使用的是 sed s/^#/20/g,其中,^代表匹配开头,^#指的是以^#开头的字符串,将^#转换成20,其中g的意思是替换的意思。
这样就替换成了20190428 11:47:54
6.现在就剩最后一步了,也就是转化成时间格式,这里我们使用linux中的date命令:
[root innodblog]# date +"%Y-%m-%d %H:%M:%S" -d "20190428 1:47:17"
2019-04-28 01:47:17
至此,目标达成。
03
再来一例
上面的例子是处理一个master_binlog_pos发生的时间,接下来这个例子是在xtrabackup备份的过程中,在众多信息中过滤出master_binlog_pos,废话就不过说了,我们先来看看xtrabackup备份时候打印出来的日志,如下:
xtrabackup: Creating suspend file '/tmp/xtrabackup_log_copied' with pid '42480'
xtrabackup: Transaction log of lsn (4774004270781) to (4774008985300) was copied.
190428 00:18:15 innobackupex: All tables unlocked
innobackupex: Backup created in directory '/root'
innobackupex: MySQL binlog position: filename 'mysqlbin.000002', position 65330751
190428 00:18:15 innobackupex: Connection to database server closed
innobackupex: You must use -i (--ignore-zeros) option for extraction of the tar stream.
190428 00:18:15 innobackupex: completed OK!
我们需要在日志中,把第6行中的position过滤出来,也就是要过滤出来数字:65330751
具体的操作步骤如下:
1.首先需要定位到这一行日志,我们采用的方法依旧是grep,如下:
[dba_mysql tmp]$ grep 'MySQL binlog position:' xtra.log
innobackupex: MySQL binlog position: filename 'mysqlbin.000002', position 65330751
我们首先通过grep命令拿到目标行。
2.紧接着我们对这个命令行中的内容进行过滤,这里我们采用awk作为过滤方法,使用的命令如下:
[dba_mysql@tk-dba-mysql-194 tmp]$ grep 'MySQL binlog position:' xtra.log
| awk '{b=index($0,", position ");print substr($0,b+10)}'
65330751
可以看到,我们使用index函数作为过滤的条件,index函数的作用是在字符串中找到指定子串的位置,然后返回位置,如果指定的字串不存在,那么返回0.我们使用index返回", position"这个字符串所在的位置,把它的位置标记为变量b。然后我们使用了一个substr的函数,这个函数的用法如下:
substr(String,M,(N)) 其中,N为可选。
返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。
它的作用是打印出变量b加10个字符的位置到字符串末尾的字符,也就是" 65330751",这里需要注意,这个数字前面有一个空格,想去掉这个空格,其实很简单,就是把b+10改为b+11即可,下面给出不同的位置处处理的结果:
[dba_mysql@tk-dba-mysql-194 tmp]$ grep 'MySQL binlog position:' xtra.log
| awk '{b=index($0,", position ");print substr($0,b+10)}'
65330751
[dba_mysql@tk-dba-mysql-194 tmp]$ grep 'MySQL binlog position:' xtra.log
| awk '{b=index($0,", position ");print substr($0,b+11)}'
65330751
[dba_mysql@tk-dba-mysql-194 tmp]$ grep 'MySQL binlog position:' xtra.log
| awk '{b=index($0,", position ");print substr($0,b+9)}'
n 65330751
,这里我们通过另外一种方法去掉空格。
3.上面的结果中包含一个空格,我们要是想继续用sed来去除这个空格,可以写成如下形式:
[dba_mysql@tk-dba-mysql-194 tmp]$ grep 'MySQL binlog position:' xtra.log
| awk '{b=index($0,", position ");print substr($0,b+10)}'
| sed 's/^[ \t]*//g'
65330751
我们需要注意的是第3行,它使用了sed函数,格式为:
sed 's/^[ \t]//g'
它的意思是将每一行前导的“空白字符”(空格,制表符)删除。其中^的意思是行首,[ \t]指的是空格或者制表符。
当然,这个命令的实际操作其实我们可以使用更为简单的方法,这里我们为了给大家演示sed和grep以及awk命令,故意使用了一些不太"高效"的方法。关于这三个命令能处理的情况还有很多,后续有典型的可以继续给大家分享,今天就到这里吧。