如何让Emacs org-mode实时显示source block执行输出结果?
Org Babel源码块实时输出+回车符解析方案
问题本质
Org Babel默认执行非会话源码块时,会缓存子进程的全部stdout/stderr输出,等进程完全退出后才统一解析、插入结果区;同时默认配置会丢弃输出中的\r(回车)控制符,导致pip、wget这类依赖回车实现动态刷新的进度条,在执行完成后堆叠成多行文本,无法正常显示。
开箱即用的配置方法
不需要写自定义过滤函数的场景,直接通过内置变量和头参数即可解决:
- 优先使用会话模式:给源码块添加
:session <自定义会话名>头参数,代码会运行在持久化的对应语言REPL进程中,输出会实时刷新到缓冲区,不会等全部执行完才返回。- 针对shell类会话,确保开启
(setq comint-carriage-motion t)(该选项默认开启),即可正确解析回车符,动态进度条会正常显示,不会堆叠。
- 针对shell类会话,确保开启
- 不想用会话的场景,Org 9.5及以上版本可通过以下设置实现异步实时输出+回车解析:
给需要实时输出的源码块添加(setq org-babel-async-show-output t org-babel-result-process-carriage-returns t):async yes头参数即可,输出会边执行边写入结果区,\r对应的行覆盖逻辑会被自动处理。 - 老版本Org临时解决进度条堆叠问题:给源码块添加
:results output raw replace头参数,同时关闭输出行尾空格裁剪(setq org-babel-trim-spaces-at-eol nil),执行完成后不会丢弃\r,至少不会出现进度条堆叠的问题,只是无法做到边执行边看输出。
自定义过滤函数插入位置
如果内置配置无法满足需求(比如使用老版本Org、需要自定义输出格式),可在以下位置插入自定义filter:
- 非会话类源码块:Org Babel通过
org-babel-eval(同步)、org-babel-eval-async(异步)两个函数启动子进程,两个函数内部通过make-process/start-process创建进程,不要直接修改Org源码,用advice-add给这两个函数加around通知,在创建进程时传入自定义的:filter函数即可。filter内需要实现两个逻辑:- 将进程实时收到的输出追加到对应源码块的结果区
- 识别输出中的
\r字符,触发结果区当前行的内容替换,模拟终端的回车刷新效果
- 会话类源码块:会话进程运行在Comint模式的专用缓冲区中,直接把自定义处理函数加入
comint-output-filter-functions钩子即可,钩子函数可以拿到进程实时输出的内容,做同步写入和回车解析即可。
效果验证
可以用以下shell块测试配置是否生效,正常情况下执行后会看到进度在同一行从1/10动态刷新到10/10,最后输出完成提示,不会出现多行堆叠:
#+begin_src shell :results output for i in {1..10}; do echo -ne "当前进度: $i/10\r" sleep 0.5 done echo -e "\n执行完成" #+end_src
内容的提问来源于stack exchange,提问作者GuPe
相关产品推荐
相关产品推荐

