灵犀指 之 sed 妙用

前奏

今天又来了一个比之前更有意思的一个需求。

需要提取两项数据,1. 包含两层目录下面的不带后缀的文件名;2. 这些文件的修改时间。
然后将这两个关联的数据插入一个表中,供后续和其他表关联查询使用。

1
2
3
4
find . -name "*.zip" -print  | xargs ls --full-time | head -n 3
-rw------- 1 root root 4792 2019-02-18 09:27:47.113037320 +0800 ./5/0/02ef1fefebcef1fddf.zip
-rw------- 1 root root 11794 2019-03-19 10:04:15.777575937 +0800 ./5/0/305c3eae0cac4fcfd0.zip
-rw------- 1 root root 5813 2019-05-11 14:22:09.270733531 +0800 ./5/0/00640aa46bf544aacf.zip

如,把这三行的 2019-02-18 02ef1fefebcef1fddf 摘出来。

然后创建数据库:

1
2
3
4
5
6
7
CREATE TABLE 'tmp_code' (
`id` int(11) unsigned NOT NULL auto_increment,
`code` char(20) NOT NULL,
`ctime` char(30) NOT NULL,
PRIMARY KEY (`id`),
KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

最后塞进去数据库。

思路

我的第一反应是: 先使用命令把

1
2
3
2019-02-18 02ef1fefebcef1fddf 
2019-03-19 305c3eae0cac4fcfd0
2019-05-11 0640aa46bf544aacfx

拿出来,然后使用我擅长的 vim 文本编辑器批量编辑。
确实也是可以实现。只需要使用 Ctrl + v 批量, 配合制作一两个宏。

vim 宏拼接sql 技巧

比如录制宏部分可以使用, 比如单独插入一个字段

分别转化成单条插入sql(思路一)

第一行单独处理, 然后我们从第二行开始制作宏

1
2
3
4
5
6
7
8
qij
IINSERT INTO tmp_tb (code) values ('
Ctrl+[
A');
Ctrl+[
q
-- 查看总行数n, 移动光标在第一行时,普通模式下执行(n-2)次回放宏:如 n = 100, 则执行时是 98@i
n-2@i

或者vim批量编辑拼接成 insert sql (思路2)

录制宏:

1
2
3
4
5
6
q
ijI'
Ctrl+[
A',
Ctrl+[
q

查看总行数n, 移动光标在第一行时,普通模式下执行(n-1)次回放宏:如 n = 100, 则执行时是 99@i

1
n-1@i

首行 输入

1
2
insert into tmp_tb code values (
);

然后把结尾的 ); 放到最后一行

得高人指点 awk 和 sed 精要

这里转而使用 awk 和 sed 实现一行搞定, 具体可以这样:

1
find . -name "*.zip" -print  | xargs ls --full-time | awk '{print $6,$9}' | sed -r 's#.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/(\S+)\.zip$#INSERT\ INTO\ tmp_tb\ \(ctime,code\)\ values\ (\1 \2)#' >> tmp_code.sql
  1. 逐一备注解释一下:
1
find . -name "*.zip" -print

是为了 找到当前目录及子目录中所有的 .zip 结尾的文件。

  1. “|” 是管道命令。
  2. 紧接着将文件的更改时间打印出来 xargs ls –full-time
  3. 然后,使用 awk awk中介绍过 的 awk 命令,默认空格作为分割符,将文本分离出来, $6, $9分别是空格分割的这么多列里,第六,第九列分别就是时间和文件信息的字符串。此时,时间已经拿到年月日,但是第九列却是带路径带后缀 ./5/0/02ef1fefebcef1fddf.zip 这样的字符串。
  4. 然后就到 sed 粉墨登场:
1
sed -r 's#.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/(\S+)\.zip$#INSERT\ INTO\ tmp_tb\ \(ctime,code\)\ values\ (\1 \2)#'

sed 有一个绝妙的地方,就是可以使用像 (匹配模式) 这样的模式,匹配起来, 然后 使用 \1 , \2 这样拿到前面的匹配模式里原文的匹配的字符串,可以理解为一个变量。 单条 sed 一共可使用 9 个这样的匹配模式。

于是就可以完成这个需求了。 对于成千上万,特别是超过 vim 能承受的打开速度和编辑的文件来说,awk 和 sed 配合 其他比如 find, grep, xargs 等命令,简直是犹如神助!

  1. 最后用
    1
    >> tmp_code.sql

把终端的标准输出都导入一个 tmp_code.sql 文件里面去。

  1. 数据库
    1
    source tmp_code.sql

入库。收工。
人呀,只要你给机会,他什么事情都能干得出来。