hexo博客自动上传脚本实现

我来详细解释 create_post.sh 脚本的实现原理。首先让我查看这个脚本文件的内容。

[1 tool called]

现在我来详细解释 create_post.sh 脚本的实现原理,包括每一个语句和变量的含义:

create_post.sh 脚本详细解析

1. 脚本头部和路径配置

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

# Hexo Blog Post Creator Script
# This script automates the process of creating and publishing Hexo blog posts

# Set Git path for Windows
GIT_PATH="/c/Program\ Files/Git/cmd"

# Set Typora path for Windows
TYPORA_PATH="/e/typora/Typora/Typora.exe"

解释

  • #!/bin/bash - Shebang,指定使用bash解释器执行脚本
  • GIT_PATH - Git安装路径变量,用于Windows环境下的Git命令
  • TYPORA_PATH - Typora编辑器路径变量,用于自动打开Markdown文件
  • 反斜杠转义 \ 用于处理路径中的空格

2. 彩色输出函数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Function to display colored output
print_message() {
echo -e "\033[1;34m[INFO]\033[0m $1"
}

print_success() {
echo -e "\033[1;32m[SUCCESS]\033[0m $1"
}

print_error() {
echo -e "\033[1;31m[ERROR]\033[0m $1"
}

print_warning() {
echo -e "\033[1;33m[WARNING]\033[0m $1"
}

函数详解

  • print_message() - 蓝色信息输出,\033[1;34m 设置蓝色粗体,\033[0m 重置颜色
  • print_success() - 绿色成功输出,\033[1;32m 设置绿色粗体
  • print_error() - 红色错误输出,\033[1;31m 设置红色粗体
  • print_warning() - 黄色警告输出,\033[1;33m 设置黄色粗体
  • $1 - 第一个参数,即要输出的消息内容

3. 用户输入获取函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Function to get user input
get_user_input() {
local prompt="$1"
local var_name="$2"
local default_value="$3"

if [ -n "$default_value" ]; then
echo -n "$prompt (default: $default_value): "
else
echo -n "$prompt: "
fi

read -r input
if [ -z "$input" ] && [ -n "$default_value" ]; then
eval "$var_name=\"$default_value\""
else
eval "$var_name=\"$input\""
fi
}

参数详解

  • $1 - 提示信息
  • $2 - 变量名
  • $3 - 默认值

局部变量

  • local prompt - 局部变量,存储提示信息
  • local var_name - 局部变量,存储变量名
  • local default_value - 局部变量,存储默认值
  • input - 用户输入的内容

逻辑流程

  1. 如果有默认值,显示包含默认值的提示
  2. 如果没有默认值,显示普通提示
  3. 读取用户输入到 input 变量
  4. 如果用户没有输入且有默认值,使用默认值
  5. 否则使用用户输入的值
  6. 使用 eval 将值赋给指定的变量

4. 输入验证函数

1
2
3
4
5
6
7
8
9
10
11
# Function to validate input
validate_input() {
local input="$1"
local field_name="$2"

if [ -z "$input" ]; then
print_error "$field_name cannot be empty!"
return 1
fi
return 0
}

参数详解

  • $1 - 要验证的输入内容
  • $2 - 字段名称,用于错误提示

逻辑

  • [ -z "$input" ] - 检查输入是否为空
  • return 1 - 验证失败返回1
  • return 0 - 验证成功返回0

5. 文件名清理函数

1
2
3
4
5
6
# Function to sanitize filename
sanitize_filename() {
local filename="$1"
# Remove special characters and replace spaces with hyphens
echo "$filename" | sed 's/[^a-zA-Z0-9\u4e00-\u9fff\s-]//g' | sed 's/\s\+/-/g' | tr '[:upper:]' '[:lower:]'
}

处理流程

  1. sed 's/[^a-zA-Z0-9\u4e00-\u9fff\s-]//g' - 移除除字母、数字、中文字符、空格、连字符外的所有字符
  2. sed 's/\s\+/-/g' - 将连续的空格替换为单个连字符
  3. tr '[:upper:]' '[:lower:]' - 将大写字母转换为小写

正则表达式详解

  • [^...] - 否定字符集,匹配不在括号内的字符
  • \u4e00-\u9fff - Unicode范围,匹配中文字符
  • \s - 匹配空白字符
  • \s\+ - 匹配一个或多个空白字符

6. 标签处理函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Function to process tags properly
process_tags() {
local tags_input="$1"
local processed_tags=""

# Split by comma and process each tag
IFS=',' read -ra TAG_ARRAY <<< "$tags_input"
for tag in "${TAG_ARRAY[@]}"; do
# Trim whitespace from tag
tag=$(echo "$tag" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$tag" ]; then
if [ -z "$processed_tags" ]; then
processed_tags="$tag"
else
processed_tags="$processed_tags\n - $tag"
fi
fi
done

echo "$processed_tags"
}

变量详解

  • tags_input - 输入的标签字符串
  • processed_tags - 处理后的标签字符串
  • TAG_ARRAY - 分割后的标签数组

处理逻辑

  1. IFS=',' - 设置内部字段分隔符为逗号
  2. read -ra TAG_ARRAY <<< "$tags_input" - 将输入按逗号分割到数组
  3. 循环处理每个标签:
    • 使用 sed 去除首尾空白字符
    • 第一个标签直接添加,后续标签添加换行和缩进

7. 主脚本开始

1
2
3
# Main script starts here
print_message "Welcome to Hexo Blog Post Creator!"
echo "=========================================="

输出

  • 显示欢迎信息和分隔线

8. 获取文章标题

1
2
3
4
5
# Get post title
get_user_input "Enter post title" "post_title" ""
while ! validate_input "$post_title" "Post title"; do
get_user_input "Enter post title" "post_title" ""
done

逻辑

  • 获取文章标题,无默认值
  • 使用 while 循环确保输入不为空
  • ! validate_input - 验证失败时继续循环

9. 获取文章分类和标签

1
2
3
4
5
# Get post category
get_user_input "Enter post category" "post_category" "general"

# Get post tags
get_user_input "Enter post tags (separated by commas)" "post_tags" ""

变量

  • post_category - 文章分类,默认值为 "general"
  • post_tags - 文章标签,无默认值

10. 文件名清理

1
2
# Sanitize title for filename
sanitized_title=$(sanitize_filename "$post_title")

变量

  • sanitized_title - 清理后的标题,用于文件名匹配

11. 创建Hexo文章

1
2
3
4
5
6
7
# Create new post using hexo
if hexo new "$post_title"; then
print_success "Post created successfully!"
else
print_error "Failed to create post!"
exit 1
fi

逻辑

  • 使用 hexo new 命令创建新文章
  • 检查命令执行结果,失败时退出脚本
  • exit 1 - 以错误状态码1退出

12. 查找创建的文章文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Find the created markdown file - improved logic
print_message "Searching for created post file..."

# First try to find by exact sanitized title
post_file=""
today=$(date +%Y-%m-%d)

# Look for files with today's date and sanitized title
for file in source/_posts/*.md; do
if [[ "$file" == *"$today-$sanitized_title"* ]]; then
post_file="$file"
break
fi
done

变量

  • post_file - 找到的文章文件路径
  • today - 今天的日期,格式为 YYYY-MM-DD

查找逻辑

  1. 首先按日期和清理后的标题精确匹配
  2. source/_posts/*.md - 遍历所有Markdown文件
  3. *"$today-$sanitized_title"* - 模式匹配,查找包含日期和标题的文件

13. 备用查找策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# If not found, try to find by partial match
if [ -z "$post_file" ]; then
for file in source/_posts/*.md; do
if [[ "$file" == *"$sanitized_title"* ]]; then
post_file="$file"
break
fi
done
fi

# If still not found, try to find the most recent file
if [ -z "$post_file" ]; then
post_file=$(ls -t source/_posts/*.md | head -n 1)
if [ -n "$post_file" ]; then
print_warning "Could not find exact match, using most recent file: $post_file"
fi
fi

备用策略

  1. 部分匹配:查找包含清理后标题的文件
  2. 最新文件:使用 ls -t 按时间排序,取第一个文件

14. 文件存在性检查

1
2
3
4
5
6
if [ -z "$post_file" ] || [ ! -f "$post_file" ]; then
print_error "Could not find the created post file!"
print_message "Available files in source/_posts/:"
ls -la source/_posts/
exit 1
fi

检查条件

  • [ -z "$post_file" ] - 文件路径为空
  • [ ! -f "$post_file" ] - 文件不存在
  • 失败时列出目录内容并退出

15. 创建Front Matter

1
2
3
4
5
6
7
8
9
10
11
12
13
# Create front matter
front_matter="---\ntitle: $post_title\ndate: $(date '+%Y-%m-%d %H:%M:%S')\ncategories:\n - $post_category"

# Add tags if provided
if [ -n "$post_tags" ]; then
front_matter="$front_matter\ntags:"
processed_tags=$(process_tags "$post_tags")
if [ -n "$processed_tags" ]; then
front_matter="$front_matter\n - $processed_tags"
fi
fi

front_matter="$front_matter\n---\n\n"

变量

  • front_matter - 文章的YAML前置信息

内容结构

  • --- - YAML分隔符
  • title - 文章标题
  • date - 创建时间
  • categories - 分类
  • tags - 标签(如果有)
  • \n - 换行符

16. 文件备份和更新

1
2
3
4
5
# Backup original file
cp "$post_file" "$post_file.backup"

# Create new content with front matter
echo -e "$front_matter" > "$post_file"

操作

  1. 备份原文件到 .backup 后缀
  2. 使用 echo -e 将处理后的front matter写入文件
  3. > 重定向符覆盖原文件内容

17. 打开Typora编辑器

1
2
3
4
5
6
7
8
9
# Open Typora for editing
print_message "Opening Typora for editing..."
if [ -f "$TYPORA_PATH" ]; then
"$TYPORA_PATH" "$post_file" &
print_success "Typora opened successfully!"
else
print_warning "Typora not found at expected path. Please open the file manually:"
echo "File location: $post_file"
fi

逻辑

  • 检查Typora是否存在
  • 使用 & 在后台运行Typora
  • 如果找不到Typora,提示手动打开文件

18. 等待用户编辑完成

1
2
3
# Wait for user to finish editing
echo ""
read -p "Press Enter when you have finished editing and saved the file..."

功能

  • 暂停脚本执行,等待用户按Enter键
  • 给用户时间在Typora中编辑文章

19. 发布选择

1
2
3
# Ask user if they want to publish
echo ""
get_user_input "Do you want to publish this post? (y/n)" "publish_choice" "n"

变量

  • publish_choice - 用户的选择,默认值为 "n"

20. 发布流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if [[ "$publish_choice" =~ ^[Yy]$ ]]; then
print_message "Starting deployment process..."

# Generate static files
print_message "Generating static files..."
if hexo g; then
print_success "Generation completed!"
else
print_error "Generation failed!"
exit 1
fi

# Deploy to server
print_message "Deploying to server..."
if hexo d; then
print_success "Deployment completed successfully!"
print_success "Your post has been published!"
else
print_error "Deployment failed!"
exit 1
fi

正则表达式

  • ^[Yy]$ - 匹配以Y或y开头和结尾的字符串

发布步骤

  1. hexo g - 生成静态文件
  2. hexo d - 部署到服务器
  3. 每个步骤都有错误检查和提示

21. 清理和完成

1
2
3
4
5
6
7
# Clean up backup file
if [ -f "$post_file.backup" ]; then
rm "$post_file.backup"
fi

echo ""
read -p "Press Enter to exit..."

操作

  1. 删除备份文件
  2. 等待用户按Enter键退出脚本

脚本工作流程总结

  1. 初始化 - 设置路径和定义函数
  2. 用户输入 - 获取文章标题、分类、标签
  3. 创建文章 - 使用Hexo命令创建新文章
  4. 文件查找 - 智能查找创建的文章文件
  5. 内容更新 - 添加YAML前置信息
  6. 编辑阶段 - 打开Typora让用户编辑
  7. 发布选择 - 询问是否立即发布
  8. 部署发布 - 生成静态文件并部署
  9. 清理完成 - 清理临时文件并结束

这个脚本通过自动化流程大大简化了Hexo博客文章的创建和发布过程,提高了工作效率。