Hugo 永久链接默认使用 Markdown 文件名作为 slug,文件名不易辨认(如 example.md),如果使用中(如 示例.md),slug 可能变成编码字符串,仍然不直观。通过配置 :slugorcontentbasename,可优先使用 Front Matter 中的 slug 字段,保持易读的文件名和简洁的 URL。
下面介绍配置修改方法,以及一个无需依赖的 Python 脚本,批量为现有文章添加 slug 并重命名。
准备工具
在开始之前,准备以下工具:
- Python(必需):用于运行批量处理脚本,安装时勾选 Add Python to PATH。
- Git(必需):用于拉取代码和版本管理。
- VS Code(可选):用于查看和编辑脚本、配置文件和 Markdown 文件。
配置永久链接
修改主题配置文件:
1
2
| [Permalinks]
posts = ":slugorcontentbasename"
|
:slugorcontentbasename 需要 Hugo 版本 ≥ v0.144.0。- 如果 Front Matter 没有
slug 字段,Hugo 默认会使用文件路径生成 URL。
保存配置后,无需其他更改,新的文章可通过 slug 自定义 URL,旧文章保持不变。
创建处理脚本
⚠️ 注意:脚本默认 Front Matter 使用 --- 包裹,且格式规范。
创建以下文件:
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
| #!/usr/bin/env python3
"""
批量处理 Markdown 文件:
1. 为没有 slug 字段的文件添加 slug: "原文件名"(插入在 title 行之后,保持原格式)
2. 将文件重命名为 Front Matter 中的 title(自动处理非法字符)
用法:
python scripts/slug-rename.py # 处理当前目录
python scripts/slug-rename.py dir1 dir2 ... # 处理多个指定目录
"""
import re
import sys
from pathlib import Path
def sanitize_filename(name):
"""移除 Windows/Linux 文件名中的非法字符"""
return re.sub(r'[\\/*?:"<>|]', '_', name)
def add_slug_to_frontmatter(content, slug_value):
"""
在 Front Matter 的 title 行之后插入 slug: "value"
保持原有格式(缩进、引号、数组写法等)
"""
pattern = r'(---\n)(.*?\n)(---\n)'
match = re.match(pattern, content, re.DOTALL)
if not match:
return content
frontmatter = match.group(2)
rest = match.group(3) + content[match.end():]
title_line_match = re.search(r'^(title\s*:\s*.+)$', frontmatter, re.MULTILINE)
if not title_line_match:
return content
title_line = title_line_match.group(0)
indent = re.match(r'^(\s*)', title_line).group(1)
slug_line = f'{indent}slug: "{slug_value}"'
new_frontmatter = frontmatter.replace(title_line, title_line + '\n' + slug_line, 1)
return f'---\n{new_frontmatter}{rest}'
def process_md_file(filepath):
print(f"正在处理: {filepath}")
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
if not content.startswith('---'):
print(f" 跳过(无 Front Matter)")
return
fm_match = re.match(r'---\n(.*?)\n---', content, re.DOTALL)
if not fm_match:
print(f" 跳过(Front Matter 格式异常)")
return
frontmatter = fm_match.group(1)
if re.search(r'^slug\s*:', frontmatter, re.MULTILINE):
print(f" 跳过(slug 已存在)")
return
title_match = re.search(r'^title\s*:\s*(.+)$', frontmatter, re.MULTILINE)
if not title_match:
print(f" 跳过(无 title 字段)")
return
title = title_match.group(1).strip()
if (title.startswith('"') and title.endswith('"')) or (title.startswith("'") and title.endswith("'")):
title = title[1:-1]
slug_from_filename = filepath.stem
new_content = add_slug_to_frontmatter(content, slug_from_filename)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(new_content)
new_filename = sanitize_filename(title) + filepath.suffix
new_filepath = filepath.parent / new_filename
if filepath.name != new_filename:
if new_filepath.exists():
print(f" 警告:目标文件已存在,跳过重命名 {filepath.name} -> {new_filename}")
else:
filepath.rename(new_filepath)
print(f" ✅ 处理完成:重命名为 {new_filename}")
else:
print(f" ✅ 处理完成:slug 已添加,文件名不变")
def main():
# 支持多个目录参数
if len(sys.argv) > 1:
target_dirs = [Path(p) for p in sys.argv[1:]]
else:
target_dirs = [Path.cwd()]
all_files = []
for target_dir in target_dirs:
if not target_dir.is_dir():
print(f"警告:'{target_dir}' 不是有效目录,跳过")
continue
# 使用 set 避免重复(虽然不同目录不太可能重复,但安全起见)
for md_file in target_dir.rglob('*.md'):
if md_file not in all_files:
all_files.append(md_file)
print(f"找到 {len(all_files)} 个 Markdown 文件")
for md_file in all_files:
process_md_file(md_file)
print("全部处理完成")
if __name__ == '__main__':
main()
|
脚本运行逻辑说明:
- 遍历目录:接收参数(默认为当前目录),递归查找所有
.md 文件。 - 检查 Front Matter:如果无 Front Matter 或已有
slug 字段则跳过。 - 提取
title:从 Front Matter 中获取 title 值(去除引号)。 - 插入
slug:在 title 行后插入 slug: "原文件名"(保持缩进格式)。 - 重命名文件:将文件重命名为
title 内容(非法字符替换为 _),如果目标已存在则跳过。 - 输出处理结果:显示每个文件的处理状态。
执行处理脚本
⚠️ 注意:使用前先备份,或先测试后批量处理,操作不可逆。
运行以下命令:
1
| python scripts/slug-rename.py content/posts
|
脚本会递归处理该目录下的所有 .md 文件,完成后运行 hugo server 启动本地预览,确认文章 URL 保持原来的短标识(如 /posts/hugo-build/),同时检查文件名已更新为可读标题。
故障回退(如发现问题):
- 如果没有提交更改,可以使用 Git 恢复:
git checkout -- content/posts/。 - 如果已经提交但未推送,可以回退到上一个提交:
git reset --hard HEAD~1。
后续新建文章
创建文章时,可自由指定文件名,并在 Front Matter 中添加 slug 控制 URL。例如:
1
| hugo new posts/tech/这是标题.md
|
打开 Markdown 文件,在 Front Matter 中添加 slug 参数即可:
1
2
3
| ---
slug: "my-custom-url"
---
|