用 C# 设置 Hugo Aliases
2020年1月5日 · 898 字 · 2 分钟 · Csharp Hugo
2020 年新年好!
相信细心的读者已经注意到了,本博客的文章链接已经全部从 blog.batkiz.com/:year/:month/:day/:filename/
调整为了 blog.batkiz.com/posts/:year/:filename/
的 url 格式。主要的原因是 url 的可读性:一则我个人认为让完整的日期出现在文章的 url 中对“人”非常之不友好。比如我曾想直接输入文章链接访问文章时,日期极大地为我原本就不太好的记忆力增加了负担。另一方面,具体而完整的时间对于读者也是不必要的,几乎没有人会关注文章是在几月几号几点写的,作为读者,更关注的点在于内容,在 url 中隐藏掉具体的时间,只给出一个年份,在我看来是某种程度上的平衡。
其实这个想法早在将博客生成器由 hexo 切换到 hugo 时就有了,但是由于兼容性的问题(我在煎蛋等平台上的投稿有到我文章的链接),我不能让原本的文章给 404 掉,所以当时只能忍辱负重,继续使用含有完整日期的 url。(见此文)
而在某天晚上我刷牙洗漱时,我突然想到,能否将 url 更改的同时,设置以带有完整日期链接的访问跳转到新的链接。经过一番搜索,我找到了名为 Aliases 的功能,这正是我要找的!
但是,在打算用这个功能时,我犯了难:博客经过三年多的积累,文章的量并不是我能够手动一篇一篇的修改的了的。当然啦,作为一个程序员,第一时间想到的,当然就是通过写代码来自动化啦。
通过观察,所添加的 Aliases 项应被添加在 frontmatter 中,其主要依赖了文章的日期即 date 项。我最开始的想法是,将每篇文章的 frontmatter (在我的文章中用的是 yaml)序列化,对序列化后的项进行修改,最后再反序列化,保存至文章。
但是由于我并没有找到一个可用性较高的解析 markdown 文件 yaml frontmatter 的库,此想法只好作罢。
经过我的进一步观察,每篇文章的 date 项均处在第三行,且格式为
date: yyyy-mm-dd time
事情就好办了起来。
核心思想即:读入文本,将位于第三行的日期解析出来->生成 Aliases 项->写入文本。
下面是代码(C# 8, dotnet core 3.1),由于时间匆忙,且需求简单,因此未做过多的处理。
using System.Linq;
using System.IO;
using System;
namespace AddAliasToBlogPosts
{
class Program
{
static void Main(string[] args)
{
string dir = @"C:\code\blog-contents\content\posts";
var posts = GetPosts(dir);
foreach (var item in posts)
{
AppendAlias(GetAlias(item), item, 3);
//System.Console.WriteLine(GetAlias(item));
}
}
static string[] GetPosts(string dir) => Directory.GetFiles(dir);
static string GetFilesName(string path)
{
path = path.Split('\\').Last();
return path.Substring(0, path.Length - 3);
}
static string GetLine(string text, int lineNo)
{
string[] lines = text.Replace("\r", "").Split('\n');
return lines.Length >= lineNo ? lines[lineNo - 1] : null;
}
static Func<string, string> GetTime = (path) =>
{
string text = File.ReadAllText(path);
string time = GetLine(text, 3);
return time.Substring(6, 10).Replace('-', '/');
};
static Func<string, string> GetAlias = (path)
=> $"\naliases:\n - /{GetTime(path)}/{GetFilesName(path)}/";
static void AppendAlias(string newText, string fileName, int line_to_edit)
{
string[] arrLine = File.ReadAllLines(fileName);
arrLine[line_to_edit - 1] += newText;
File.WriteAllLines(fileName, arrLine);
}
}
}
最后,感谢 MSDN 与 StackOverflow。