利用 Shell 脚本高效管理博客:从创建到推送

前言

之前我曾写过一篇关于使用 NPM 和 Shell 管理 Hexo 博客的文章,点击这里跳转。虽然那是我的第一篇文章,写得不是很完善,但脚本还是可以用的。最近我将博客平台切换到了 Hugo,因此决定对内容进行重构,以便更好地适应新的平台。

正文

大概的思路其实没有变,都是简化命令的输入。

先写一个 case 语句:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
case $1 in
    new)
    echo "new blog"
    ;;
    push)
    echo "push blog"
    ;;
    *)
    echo "unknown option"
    exit 1
    ;;
esac

new 这个分支主要是调用hugo new content [path]生成 md 文件,需要解决根据传入的标题变量自动生成路径和文件名重复。

文件路径为了兼容就沿用之前的吧:content/posts/[yyyymmdd]/[title]/[hash-12].md

时间类似于:20240720,使用date +"%Y%m%d"

标题为了省事直接限定只能包含大小写字母和-,使用grep -Eq '^[0-9a-zA-Z-]+$'-E表示正则匹配,-q表示静默模式,通过返回的状态码判断是否匹配,匹配返回0,不匹配返回1

文件名为了保持之前的风格,使用dd if=/dev/urandom bs=1 count=6 2>/dev/null | od -An -tx1 | tr -d ' ',它会生成12个随机字符串

时间加上随机值大概率是不会出现重复的,为了避免意外导致文章被覆盖,可以使用if语句和-e选项来判断:

1
2
3
4
5
# 抽取为一个函数
if [ -e "path"]; then
    # 存在 重新生成文件名 递归调用自己
else
    # 不存在 调用hugo new

难点在于函数的拆分和递归出口条件的判断,实现以下函数:

 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
# 函数:检查标题是否合法
check_title() {
    echo "$1" | grep -Eq "^[0-9a-zA-Z-]+$"
}

# 函数:生成随机文件名
generate_random_filename() {
    echo $(dd if=/dev/urandom bs=1 count=6 2>/dev/null | od -An -tx1 | tr -d ' ')
}

# 函数:根据标题生成完整路径
generate_path() {
    local title=$(echo "$1" | tr ' ' '-')
    local random_filename=$(generate_random_filename)
    local date=$(date +"%Y%m%d")
    echo "content/posts/$date/$title/$random_filename.md"
}

# 函数:创建博客文件,处理递归的出口条件
create_blog_file() {
    local title="$1"
    local path=$(generate_path "$title")

    # 如果文件已经存在,则递归调用自己
    if [ -e "$path" ]; then
        create_blog_file "$title"
    else
        # 文件不存在,创建文件
        hugo new content "$path"
    fi
}

这样只需要在 new 分支调用check_titlecreate_blog_file就可以了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
...
    new)
    if check_title "$2"; then
        create_blog_file "$2"
    else
        echo "标题格式:只能包含大小写字母,数字,-"
        exit 1
    fi
    ;;
...

而 push 分支需要做的就是 add + commit + push,这里会有一个问题,如果开着代理的话是 push 不上去的(不知道是不是节点的问题),而本地仓库已经有一条 commit 记录了,这时候需要git resetgit commit,特别的麻烦,为了保持远程仓库提交记录的干净,需要使用git log origin/main..HEAD --oneline来判断本地是否存在已经提交但未推送的记录,如果有则输出不为空,完成 push 分支:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 函数名:add + commit + push
acp(){
    git add .
    git commit -m "update:blog $(echo $1 | tr ' ' '-') $(date +%Y%m%d)"
    git push
}
...
    push)
    # wc -l 获取输出的行数
    unpushed_commits_count=$(git log origin/main..HEAD --oneline | wc -l)
    if [ "$unpushed_commits_count" -gt 0 ]; then
        git reset --soft origin/main # TODO: mixed soft hard 区别
        acp "$2"
    else
        acp "$2"
    fi
    ;;
...

后记

Shell 脚本还是挺好玩的,前提是不出 bug 哈哈哈