惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Scott Helme
Scott Helme
N
Netflix TechBlog - Medium
AI
AI
Security Latest
Security Latest
GbyAI
GbyAI
P
Proofpoint News Feed
Y
Y Combinator Blog
A
Arctic Wolf
G
Google Developers Blog
U
Unit 42
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Vulnerabilities – Threatpost
Know Your Adversary
Know Your Adversary
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
C
Check Point Blog
B
Blog RSS Feed
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cisco Blogs
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
Latest news
Latest news
L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
WordPress大学
WordPress大学
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
Project Zero
Project Zero
博客园_首页

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
使用 Workflow 批量导出 Ulysses 文稿 - 少数派
2017-08-15 · via 少数派

Ulysses 最近刚刚改为订阅制收费,有很多人都在考虑转到其他 Markdown 编辑器。今天看到 Checked 听众群里有人说 iOS 上 Ulysses 不能批量导出,所以我想讲一下如何利用 Ulysses 的 URL Scheme 和 Workflow 批量导出 Ulysses 中的文稿。

Ulysses 是一款很优秀的 Markdown 写作 App,优点之一就是丰富的 URL Scheme 支持。在 Ulysses 的官网上可以看到 URL Scheme 官方文档。利有 URL Scheme 和 Workflow 就可以导出 Ulysses 的所有文档。

批量导出 Ulysses 文稿效果图

  • 批量导出 Ulysses 中的文稿到 iCloud Drive 或 Dropbox 并保留文稿库的文件夹结构。
  • 文档内容为 Markdown 格式,但因为 Workflow 的限制导出的文件后缀为 .txt。
  • 这个 Workflow 会忽视没有内容的空文稿。
  • 文稿的题目为第一个标题,在文稿内没有标题的情况下为文章开头。

Workflow 怎么使用?

下载 Export Ulysses 这个 Workflow 并运行,Workflow 会请求获取 Ulysses 文稿库的权限。之后会需要选择保存到 iCloud Drive 还是 Dropbox。在这里需要注意:如果保存到 Dropbox 请一定保证文稿题目不重复,如果在同一文件夹下有重复会覆盖重名文件。并且,保存到 iCloud Drive 的文件名去掉了 Markdown 格式(比如 #),而 Dropbox 并不能去掉 Markdown 格式。Workflow 根据文稿数量可能会运行一段时间。

Workflow 的制作之旅

可能有一些人知道我最初发出来的批量导出 Ulysses 的 Workflow 有三个,后来我改成了两个,而现在仅有一个 Workflow 来完成全部的导出工作。我也从想要写一篇单纯的介绍 Workflow 怎么用怎么做的文章,到想讲一讲我这三次做 Workflow 的过程中遇到的 Workflow 的局限、我在这个 Workflow 中如何用一些小技巧绕过了这些局限,以及我对于 iOS 设备的「生产力」的看法。

Ulysses URL Schemes

Workflow 有内置 Ulysses 的动作,但是不足以实现导出功能。所以整个 Workflow 将使用 URL Schemes 实现。要实现这个 Workflow 我们先要了解 Ulysses 的 URL Schemes,可以在官网查到详细的介绍。(如果对于 URL Schemes 不了解可以先看 JailbreakHum 的「URL Schemes 使用详解」)其中我们要用到的 URL Schemes 有以下几个:

  • Authorization

ulysses://x-callback-url/authorize?appname=[Name]

这个 URL Scheme 可以获取 Access TokenName 的部分填入 Workflow 即可。

  • get-root-items

ulysses://x-callback-url/get-root-items?recursive=[YES/NO]&access-token=[Access Token]

这个 URL Scheme 可以获取 Ulysses 文稿库的全部列表。recursive 是 Optional 的,如果填入 YES,结果将是一个有深度的列表。因为我们想要保留 Ulysses 的文件夹结构,所以这里填 YES。

在 get-root-items 这个动作中,对于每一个 item 返回的 JSON 都会包含有两个 key,分别是 containers 以及 sheets。这两个 key 的 value 又分别会是一个 dictionary,包含了更深一层级的信息。

  • read-sheet

ulysses://x-callback-url/read-sheet?id=[identifier]&text=[YES/NO]&access-token=[Access Token]

这个 Workflow 可以读取特定 Sheet 的标题、内容、Keywords 和 Notes 等。identifier 是 Sheet 的 ID,在 get-root-items 中可以获得。text 是是否获取文稿内容,默认是 NO,这里我们要填 YES。

Workflow 思路

(注:Workflow 思路涉及到编程算法,我已经尽力解释得简单易懂。如果不感兴趣可以直接看结尾。)

Ulysses 中允许文件夹无限层级,这就形成了一个树状结构。想要对于树状结构中的每一项都进行导出动作并不是一件轻松的事情。为什么这样说呢?当你获取到某一个文件夹的信息时,你可以知道这个文件夹下面还有没有子文件夹,但是你很难知道子文件夹下还有没有子文件夹,一共有几个文件夹。

当我们使用 Workflow 的时候,你很难轻松让 Workflow 做到「一直导出到没有子文件夹」这件事情。「一直导出到没有子文件夹」等于「一直循环导出,直到没有子文件夹时停止循环」。想要让 Workflow 一直循环是做不到的,Repeat with Each 动作是对一个有确定数目的列表执行,而 Repeat 动作也需要输入一个具体的数值。但是在这里我们可以使用 Repeat 并填入一个非常大的数来间接做到这一点,比如一千或一万。我想绝大部分人也不会在 Ulysses 里有上千上万个文件夹吧。

但是后半句「直到没有子文件夹时停止循环」是怎么也做不到的,Workflow 可以检测出有没有子文件夹,但是却缺失了一个关键的动作是「停止循环」

这时候我们需要使用两个编程算法中的思路,分别叫深度优先搜索和广度优先搜索。这两个思路(或者说算法)都是用来遍历树状结构的。其含义很简单,深度优先搜索就是对于一棵树优先向深处走,而广度优先搜索则是对于一棵树优先走完同一层级的节点。从下面的 GIF 图可以很清楚地看出来这个区别。

深度优先搜索(左)和广度优先搜索(右)

使用深度优先搜索实现 Workflow

深度优先搜索是我最先使用的思路,这个思路可以很简单地就解决导出 Ulysses 文稿这个需求。我实现这个思路使用了三个 Workflow 来完成,接下来会讲到为什么需要三个 Workflow 来完成,以及我如何把 Workflow 缩减为两个的。考虑到并不是每一个读者都了解编程,我这里使用文字的形式来描述一下这个思路实现到 Workflow 中时整个 Workflow 的结构。

批量导出 Ulysses 文稿 Workflow

  在 iCloud Drive 中新建一个名为 Ulysses 的文件夹

  使用 get-root-items 获取整个文稿库的 JSON

  对于 JSON 中的每一个 Container(这些 Container 是 Ulysses 的主文件夹)   运行「导出文件夹」Workflow

  结束

导出文件夹 Workflow

  在 iCloud Drive 的 Ulysses 文件夹下新建当前 Ulysses 文件夹对应的文件夹

  检测当前 Ulysses 文件夹内有没有子文件夹

  如果有子文件夹

  对于每一个子文件夹

  运行「导出文件夹」Workflow

  检测当前 Ulysses 文件夹内有没有文稿

  如果有文稿

  对于每一篇文稿

  获取文稿 identifier

  运行 read-sheet 获取文稿内容

  存入 iCloud Drive

  结束

可以看到,「导出文件夹」这个 Workflow 内也运行了「导出文件夹」Workflow,这种自己运行自己的行为在编程中叫做「递归」。因为会有自己运行自己的情况,所以「导出文件夹」这个 Workflow 必须独立出来以便于被运行。简而言之,这个 Workflow 会不断检查有没有子文件夹,把全部子文件夹导出之后再把当前文件夹导出。

如果发现子文件夹就对子文件夹执行 Export Container

在这个过程中,有一个很重要的变量是当前文件夹的路径。通过 JSON 我们可以知道当前文件夹的名字,但是我们无法知道这个文件夹是谁的子文件夹。如果不知道当前文件夹的完整路径,在保存文稿的时候我们就不知道应该把文稿保存在什么路径下。

我使用的是剪贴板来存储当前文件夹的完整路径。在「批量导出 Ulysses 文稿」这个 Workflow 中把剪贴板设置为 /Ulysses/。运行「导出文件夹」时,Workflow 会把当前文件夹的名字添加到剪贴板后面。比如当前文件夹是 A,那剪贴板就会变为 /Ulysses/A/,如果在 A 文件夹下有 B 子文件夹,对 B 运行「导出文件夹」Workflow 时剪贴板就会变为 /Ulysses/A/B/。导出文稿时的保存路径时填入剪贴板了。

将当前文件夹名称添加到剪贴板后面

如果导出完 B 文件夹要回到导出 A 文件夹的 Workflow 时,剪贴板还是 B 文件夹的完整路径。但是导出 A 文件夹内的文稿时需要的是 A 文件夹的完整路径,也就是说,我们需要在「导出文件夹」这个 Workflow 运行完之后把剪贴板复原为运行 Workflow 之前的状态。也就是,删除在剪贴板末尾的当前文件夹的名字。我们可以使用 Workflow 的 Replace Text 动作把名字替换为空白做到这一点。

那为什么需要第三个 Workflow 呢?是因为上面这两个 Workflow 中用到的 Ulysses URL Schemes get-root-items 和 read-sheet 都需要用到 Access Token。并没有很好的办法可以做到让这两个 Workflow 都有同一个 Access Token。于是只能单独做一个获取 Access Token 并复制到剪贴板的 Workflow 来解决这个问题,并让用户在使用这个 Workflow 前分别填入 Access Token

而后来我通过在主 Workflow「批量导出 Ulysses 文稿」中获取 Access Token,并向「导出文件夹」这个 Workflow 以列表形式同时传入 Container 的 JSON 和 Access Token 的方式把 Workflow 缩减为了两个。

你可以下载 Export Ulysses 和 Export Container 这两个 Workflow 看 Workflow 实现细节。

使用广度优先搜索实现 Workflow

深度优先搜索的思路必须使用最少两个 Workflow 来完成全部的工作,而广度优先搜索则可以使用一个 Workflow 做到。但是我一开始一直认为广度优先搜索在 Workflow 上是不可实现的。为什么不可实现?这个 Workflow 我又是怎么实现的?这要从广度优先搜索的概念说起。

广度优先搜索是先遍历同一层级的文件夹,然后再遍历下一个层级的所有文件夹,这样不断加深层级的一种遍历树状结构的方式。从上文的 GIF 图中可以看到广度优先搜索的遍历顺序。广度优先搜索在这个 Workflow 需求上的好处是,因为不需要自己运行自己的 Workflow,所以可以只使用一个 Workflow 来实现。

树状图

我们就以这个树状图为例,走到 4 节点之后我们应该怎么走到 2 节点下的 5 节点呢?在深度优先搜索的思路中,因为是父文件夹和子文件夹的关系,从 2 节点可以直接走到树状图的 5 节点(对于深度优先搜索你可以联想一下盗梦空间)。而在广度优先搜索的思路中,我们需要使用一个「待处理」队列来记录我们之后需要处理的节点。

这个「待处理」队列以上面的树状图为例实际效果是:先看队列中的第一项,也就是 1 节点。从 1 节点我们知道 1 节点有 2、3、4 子节点,并全部加入队列。之后看队列的第二项,也就是刚加入队列的 2 节点,我们从 2 节点可以知道 2 节点有 5 子节点,并把 5 节点加入队列。这样反复两次之后,队列下一项是 5 节点,我们就做到了处理完 4 节点之后就跳到 2 节点的子节点 5 节点的效果了。

所以 Workflow 中我们应该对于这个队列做类 Repeat with Each 动作。而随着我们每处理一个文件夹,这个文件夹的子文件夹就会被加到队列的末尾。也就是这个队列会随着处理逐渐变长。但是因为 Repeat with Each 只能对一个固定项目的变量进行循环处理,所以 Workflow 应该是实现不了这个思路的。但是这里我们可以用一个取巧的做法。队列最终的长度,也就是 Ulysses 中一共有多少文件夹,可以通过匹配 Ulysses 返回的 JSON 中匹配 “type”:”group” 的个数来知道。而队列的长度就是我们一共要循环的次数,所以使用 Repeat 并填入匹配到的数量就可以了。

所以整个 Workflow 的结构是:

批量导出 Ulysses 文稿 Workflow

  获取 Ulysses Access Token

  选择储存的服务并在其中创建 Ulysses 文件夹

  使用 get-root-items 获取整个文稿库的 JSON

  匹配出一共有多少个文件夹

  把 Ulysses 的主文件夹加到队列(如同上例的 1 节点)

  循环 文件夹数 次

  对于队列中的 Repeat Index 的项(每项就是一个文件夹)

  创建 Ulysses 文件夹的同名文件夹

  判断文件夹中有没有子文件夹

  如果有子文件夹

  把每一个子文件夹加入队列末尾

  判断文件夹中有没有文稿

  如果有文稿

  把文稿导出到对应文件夹

  结束

深度优先搜索中很重要的一个变量就是文稿要保存的路径。这个变量在这个 Workflow 中是在队列中和文件夹一起记录的。文件夹的路径和文件夹的 JSON 作为一个 dictionary 保存在队列中。这样从文件夹的 JSON 获取到文稿的同时,也能知道当前文件夹的路径。

你可以下载 Export Ulysses 查看具体实现细节。