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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
#!/usr/bin/env python3
"""
Hugo Frontmatter 转换脚本
将 YAML 格式 (---) 转换为 TOML 格式 (+++)
"""
import os
import re
import sys
from pathlib import Path
import yaml # pip install pyyaml
import tomli_w # pip install tomli-w
def yaml_to_toml_frontmatter(content: str) -> str:
"""
将 YAML frontmatter 转换为 TOML frontmatter
"""
# 匹配 YAML frontmatter: ---\n...\n---
yaml_pattern = r'^---\s*\n(.*?)\n---\s*\n'
match = re.match(yaml_pattern, content, re.DOTALL)
if not match:
return None # 不是 YAML frontmatter 格式
yaml_content = match.group(1)
body_content = content[match.end():]
# 解析 YAML
try:
data = yaml.safe_load(yaml_content)
if data is None:
data = {}
except yaml.YAMLError as e:
raise ValueError(f"YAML 解析错误: {e}")
# 转换为 TOML
# tomli_w 需要 dict,且不支持 None 值,需要处理
def clean_for_toml(obj):
if isinstance(obj, dict):
return {k: clean_for_toml(v) for k, v in obj.items() if v is not None}
elif isinstance(obj, list):
return [clean_for_toml(item) for item in obj if item is not None]
elif obj is None:
return ""
return obj
clean_data = clean_for_toml(data)
# 生成 TOML
try:
toml_bytes = tomli_w.dumps(clean_data)
toml_str = toml_bytes.decode('utf-8') if isinstance(toml_bytes, bytes) else toml_bytes
except Exception as e:
raise ValueError(f"TOML 生成错误: {e}")
# 组合新的 frontmatter
new_content = f"+++\n{toml_str}+++\n{body_content}"
return new_content
def process_file(file_path: Path, dry_run: bool = False) -> bool:
"""
处理单个文件
返回: 是否进行了转换
"""
try:
content = file_path.read_text(encoding='utf-8')
except Exception as e:
print(f"❌ 读取失败: {file_path} - {e}")
return False
# 检查是否是 YAML frontmatter (--- 开头)
if not content.startswith('---'):
# 检查是否是 TOML frontmatter (+++ 开头),跳过
if content.startswith('+++'):
print(f"⏭️ 跳过 (已是 TOML): {file_path}")
else:
print(f"⏭️ 跳过 (无 frontmatter): {file_path}")
return False
# 转换
try:
new_content = yaml_to_toml_frontmatter(content)
if new_content is None:
print(f"⏭️ 跳过 (格式不匹配): {file_path}")
return False
except ValueError as e:
print(f"❌ 转换失败: {file_path} - {e}")
return False
# 写入文件
if not dry_run:
try:
# 备份原文件
backup_path = file_path.with_suffix(file_path.suffix + '.bak')
file_path.rename(backup_path)
# 写入新内容
file_path.write_text(new_content, encoding='utf-8')
# 删除备份(可选,注释掉这行会保留备份)
# backup_path.unlink()
print(f"✅ 已转换: {file_path}")
except Exception as e:
print(f"❌ 写入失败: {file_path} - {e}")
return False
else:
print(f"[预览] 将转换: {file_path}")
# 打印前 500 字符预览
preview = new_content[:500] + "..." if len(new_content) > 500 else new_content
print(preview)
print("-" * 50)
return True
def main():
# 配置
content_dir = Path("content") # Hugo content 目录
dry_run = False # 设为 True 预览而不实际修改
# 检查命令行参数
if len(sys.argv) > 1:
if sys.argv[1] in ['--dry-run', '-n', '--preview']:
dry_run = True
print("🔍 预览模式 (不会实际修改文件)")
else:
content_dir = Path(sys.argv[1])
if not content_dir.exists():
print(f"❌ 目录不存在: {content_dir}")
sys.exit(1)
# 查找所有 markdown 文件
md_extensions = {'.md', '.markdown', '.mdown', '.mkd'}
md_files = [
f for f in content_dir.rglob('*')
if f.is_file() and f.suffix.lower() in md_extensions
]
print(f"📁 扫描目录: {content_dir.absolute()}")
print(f"📄 找到 {len(md_files)} 个 Markdown 文件\n")
converted = 0
skipped = 0
failed = 0
for file_path in md_files:
result = process_file(file_path, dry_run)
if result:
converted += 1
elif result is False:
# 区分失败和跳过
pass
print(f"\n{'='*50}")
print(f"处理完成:")
print(f" - 转换: {converted} 个文件")
print(f" - 跳过: {len(md_files) - converted} 个文件")
if dry_run:
print(f"\n💡 这是预览模式,实际运行请去掉 --dry-run 参数")
if __name__ == "__main__":
main()
|