作为一个曾经的(三流)码农,很庆幸那个年代尚无 AI 辅助编程工具,否则我应该是被 AI 取代掉的第一批。
虽然已经很多年没正儿八经碰过代码,不过今年我偶尔会让 AI 帮我写一些小工具(脚本)来实现一些小的功能想法,提高效率的同时还省钱——当然,99%的代码都是 AI 写的。
比如我做了几个快捷指令,并放在 macOS 状态栏上——就像 One Switch 做的那样——可以实现一键「隐藏桌面图标文件」、「生成二维码图」、「图片压缩」等小功能。

我还利用「自动操作」配合 Shell 脚本制作了「一键视频瘦身」的小功能,任何软件都不用打开。

今天我想分享一个 AI 帮我实现的小功能跟 Ulysses 有关系。
Ulysses 是我用了多年的 Markdown 写作工具,各方面体验都近乎完美。为数不多让我想吐槽的地方之一,就是它保存的文章不是标准的 Markdown,而是一种或许只有它们自己能看懂的文件组织和格式。

好在 Ulysses 支持丰富的导出功能,比如你可以把一篇文章用 Ulysses 导出为 .txt 纯文本、.rtfd 富文本、HDML 网页、ePUB 电子书、PDF、docx,当然也包括 Markdown。
不过如果你想要一次批量导出多篇文章,就没那么好受了,不管是导出为纯文本、富文本还是 Markdown,Ulysses 都会把你的多篇文章合并成一个文件。
比如我选择 80 篇文章,并导出为 Markdown,我们会得到如下文件结构的文件:
/
├── index.md
├── image1.jpg
└── image2.jpg
└── ...我的 80 篇文章都被合并成一个叫 index.md 的 Markdown 文件,这肯定不是我想要的,我希望每一篇文章都是单独的一个 Markdown。
那试试不用 Ulysses 提供的导出,而自己手动导出呢?下面是我派作者 Vanilla 分享的手工方法。
- 在 Ulysses 软件设置「边栏」里勾选「外部文件夹」
- 在 Ulysses 外部文件夹新建任意名的文件夹
- 把 Ulysses 的多篇文章拖到新建的文件夹,即会保存成独立的 Markdown 文件
这种方式也不完美,因为图片并不会拷贝过去,导致保存的 Markdown 文章内图片链接失效。
细心如我会发现,Ulysses 还可以导出一种叫 TextBundle 的文件格式。

我跟大家一样对 TextBundle 这种文件格式不是很眼熟,这不怪咱们,因为它并不是一种标准的文件格式,而是由 Ulysses 的开发团队和其他一些 Markdown 编辑器开发者(比如 iA Writer、Bear)一起制定的格式方案,目的就是解决 Markdown 文件传播时内嵌资源(比如图片)丢失的问题。
TextBundle 文件本质上就是一个压缩打包。用 Ulysses 导出文章合集的 .textbundle 文件的默认目录结构如下:
xxx.textbundle
├── text.md
├── info.json
└── assets/
├── image1.jpg
├── image2.png
├── ...Ulysses 仍会把多篇文章合并成一个 Markdown(这里是 text.md),assets 目录下是文章内的所有配图。

我找遍全网都没有能完美实现我想要的导出方案。好吧,那就自己动手写个脚本,把 text.md 按照一级标题 # 拆分成多篇 Markdown 文件,并把一级标题作为文件名。
脚本我可不会写,不过有 AI,这都不算事儿。ChatGPT 建议我用 Python 搞。它按照我的需求想法写了一段 Python 代码,并保存为 split_textbundle.py 这个文件。你可以在我的 GitHub 项目下载或直接复制代码。
只需要把 Ulysses 导出的 xxx.textbundle 和 AI 写的 split_textbundle.py 这两个文件放在同一个文件夹(比如名叫 split_output)内,然后执行这个 Python 就搞定了。

从没有接触过编程的朋友可以这样运行 Python 文件。打开终端,输入 cd 后面加一个空格,再把文件夹拖拽进终端窗口,然后回车,粘贴 python3 split_textbundle.py 这行代码并回车,就 OK 了。

程序会自动生成一个叫 split_output 的文件夹,里面包含每篇文章的 Markdown 文件,以及包含所有图片的 assets 文件夹。

现在我们可以把 split_output 文件夹里的所有文件——包括 Markdown 文章和保存在 assets 里的图片——备份到别的地方,或者迁移到 Typora、Obsidian 等你喜欢用的其他 Markdown 编辑写作工具中。
我还做了一个改进版(项目里的 split_textbundle_frontmatter.py),每篇文章的 Markdown 内不以一级标题 # 作为文章的标题,而是添加 front matter 的 Title 字段,并把一级标题作为 Title 的值。
文章标题会从:
# 这是文章标题变成:
---
title: "这是文章标题"
---就这样吧。
