指南页面的自动收集

最近给博客站点增加了一个独立的页面: 指南 ,用来记录本站点的所有文章,包括发表时间与文章链接,并按照发表时间倒序排列,仅供快速查找以及核对使用。其中,还会有一个文章编号,就是 url 的数字部分,由日期与编号组成,例如 2019050201 表示 2019 年 05 月 02 日发表的第 1 篇文章,这个文章编号对我来说很有用,用来核对查重。但是,为了简化这个页面的整理工作以及后续的自动生成,我需要把这个流程自动化,本文记录实现思路与实现方式。

实现思路

为了简化难度,我准备使用 Shell 解决这个问题。大概思路:遍历目录的文件、解析 idtitle、搜索匹配指南 index 文件、替换或者追加。

1、遍历指定目录【_post 目录】中的所有 markdown 文件,对每个文件执行 2。

2、对单个文件,使用 grep 正则搜索: ^id: [0-9]{10},使用 awk 获取 id 值,判断 id 值是否在 index 文件中,在则跳过,否则执行 3。

3、再使用 grep 正则搜索: ^title: ,使用 awk 获取 title 值,执行 4,把 id 值和 title 值追加到 index 文件中。

4、先使用 grep id 值 index 文件 搜索 3 中的 id 是否已经在 index 文件中,不在才能追加进去。追加 index 文件时,先重命名 index 文件为 index_bak,逐行读取。对每一行数据使用 grep 正则搜索:

1
echo $line | grep -E '^- [0-9]{10},\[' | awk -F',' '{print $1;}' | awk -F' ' '{print $2;}'

如果搜索无内容的直接将当前行写入新文件,命名为 index。再结合使用 awk 获取当前行的 id 值,只要 3 中的 id 值大于此 id 值并且年份一致,则将 3 中的 id 值和 title 值写入新文件【构造特定格式的数据行】,接着再将当前行写入新文件。如果年份一致且 3 中的 id 值最小,则按照 5 写入当年的数据最后一行。

5、相同年份作一个标记,是否已经写入新文件作一个标记,如果遇到读取的下一行已经不是当年的数据【正则搜索无结果】,则把构造的特定格式的数据行写入新文件,再把当前行写入新文件。

6、4 中的文件逐行读取完成后,把 4 中一开始重命名的文件 index_bak 删除,只保留新文件 index,此时 index 文件中已经增加了一行新内容。

具体实现

以下涉及的 Shell 脚本已经被我上传至 GitHub,读者可以提前下载查看:auto_guide.sh ,脚本命名与下文中描述一致。

Shell 脚本内容参考如下【脚本名称为:auto_guide.sh】,注释都已经标明处理逻辑,通俗易懂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/bin/bash
# index 文件
index=./source/guide/index.md
index_bak=./source/guide/index_bak.md
# 文本格式
content_pattern='- id,[title](https://www.playpi.org/id.html)'
# 遍历文件夹内的所有文件
for file in ./source/_posts/*.md
do
if [-f $file]; then
# 获取单个文件的 id【在第 3 行】 和 title【在第 2 行】
echo '================================================================'
# echo '====read file:' $file
var1=$(grep -nE '^id: [0-9]{10}' $file | grep -E '^3:id: [0-9]{10}' | awk -F': ' '{print $2;}')
# echo '====read id:' $var1
var2=$(grep -n '^title: ' $file | grep '^2:title: ' | awk -F': ' '{print $2;}')
# echo '====read title:' $var2
# 判断非空必须使用双引号,否则逻辑错误
if [-n "$var1"] && [-n "$var2"]; then
has=$(grep $var1 $index)
# 为空,表示 id 不在 index 文件中,has 变量切记使用双引号
if [-z "$has"]; then
# 字符串搜索替换,待搜索字符串是变量,不是字符串本身,// 表示替换所有
content=${content_pattern//id/$var1}
content=${content/title/$var2}
# 追加到 index 文件中
echo '====prepare append to index:' $content
# 重命名 index 文件
mv $index $index_bak
# 标记是否写入 / 是否同一年份
has_write=''
is_same_year=''
while read line
do
match_id=$(echo $line | grep -E '^- [0-9]{10},\[' | awk -F',' '{print $1;}' | awk -F' ' '{print $2;}')
# 搜索到匹配内容并且还没写入
if [-n "$match_id"] && [-z "$has_write"]; then
# echo '====compare,match_id:' $match_id
# 判断是否相同年份
if [${var1:0:4} == ${match_id:0:4}]; then
is_same_year='1'
# 比较大小
if [$var1 -gt $match_id]; then
echo '====gt match_id append to index:' $content
echo $content >> $index
echo $line >> $index
has_write='1'
else
echo $line >> $index
fi
else
echo $line >> $index
fi
elif [-n "$is_same_year"] && [-z "$has_write"]; then
# 当前行没有搜索到匹配内容,并且同一年份,并且还没写入,说明已经是当前年份的最后一行了,直接写入即可
echo '====last append to index:' $content
echo $content >> $index
echo $line >> $index
has_write='1'
else
# 没有搜索到匹配内容,或者不同年份,或者已经写入,直接写入即可
echo $line >> $index
fi
done < $index_bak
# 删除 index_bak 文件,此时只有最新的 index 文件
rm $index_bak
else
# 在 index 文件中已经存在,无需处理
echo '====index has:' $var1
fi
else
echo '!!!!invalid var:' $var1 $var2
fi
else
echo '!!!!invalid file:' $file
fi
done

执行脚本时会打印解析出来的每条 idtitle,以及写入的原因【比较后写入、最后一条写入】,第一次执行脚本耗时久一点,后续执行会自动跳过已经收集过的,耗时可以忽略。

由于实现思路简化了,所以要求 index 文件在每一年都至少已经有一个完整的记录,否则没法对比写入。

注意,正则搜索时在必要情况下使用 -E 选项开启扩展模式,否则正则无效,仍旧是普通的字符串。

日期格式的字符串比较大小,由于格式规范,都是十位数字,可以直接转为数字比较,大的就代表日期最新。

当然,日期格式的字符串比较大小,也可以先转为时间戳数字,再进行比较,示例:

1
date +% s -d '2019010101'

虽然这种格式得到的结果不是真正的时间戳,但是只要是数字就可以比较了。另外,经过查找帮助手册,没有发现可以指定格式的参数选项,也就是无法把 YYMMddHH 格式的日期字符串转为对应的真实时间戳。

虾丸派 wechat
扫一扫添加博主,进技术交流群,共同学习进步
永不止步
0%