看到这里有个例子,执行第二个 block 的时候,先行自动把第一个 block 导出为文件:
#+NAME: my_hello
#+BEGIN_SRC emacs-lisp :tangle /tmp/hello.el
(message "Hello")
#+END_SRC
#+BEGIN_SRC sh :var DUMMY=(progn (org-babel-goto-named-src-block "my_hello") (org-babel-tangle '(4))) :results output
cat /tmp/hello.el
rm -f /tmp/hello.el
#+END_SRC
#+RESULTS:
: (message "Hello")
有没有更简单(或正常一点)的方法?
如果不限定必须 tangle 指定代码块可以
C-c C-c 之前 tangle 所有 :tangle
参数值不是 no
的代码块
不清楚是不是因为这个需求本身比较怪(少见),好像没有比较正常的解决方法。
Org 代码块有个 :post
参数,可以用来在执行完当前代码块后,再执行另一个代码块:
#+NAME: upcase
#+BEGIN_SRC sh
tr a-z A-Z < /tmp/hello.txt
#+END_SRC
#+BEGIN_SRC sh :post upcase
echo hello world > /tmp/hello.txt
#+END_SRC
#+RESULTS:
: HELLO WORLD
对于这个问题,如果非要用它的话(这个比你的方法还麻烦):
#+NAME: my_hello
#+BEGIN_SRC emacs-lisp :tangle /tmp/hello.el
(message "Hello")
#+END_SRC
#+NAME: cat_hello
#+BEGIN_SRC sh :results output
cat /tmp/hello.el
rm -f /tmp/hello.el
#+END_SRC
#+BEGIN_SRC emacs-lisp :post cat_hello
(save-excursion
(org-babel-goto-named-src-block "my_hello")
(org-babel-tangle '(4)))
#+END_SRC
#+RESULTS:
: (message "Hello")
或许把你的 progn
封装成一个函数
(defun your-org-babel-tangle (src-name)
(save-excursion
(org-babel-goto-named-src-block src-name)
(org-babel-tangle '(4))))
然后再调用,或许看起会更好一点点(?)
#+NAME: my_hello
#+BEGIN_SRC emacs-lisp :tangle /tmp/hello.el
(message "Hello")
#+END_SRC
#+BEGIN_SRC sh :results output :var _=(your-org-babel-tangle "my_hello")
cat /tmp/hello.el
rm -f /tmp/hello.el
#+END_SRC
#+RESULTS:
: (message "Hello")
有时候需要使用到大段的 raw text,但不适合直接放在当前 block 中。
还有一种情况,例如 haskell 这样的语言,代码写在文件中,跟写在 repl 中,是有显著差别的,所以需要:
- block1 写常规代码,tangle 到文件;
- block2 写 repl 执行代码:加载 block1 的文件,然后调其中的方法。
所以就想有没有一种比较方便的操作,只需在 block2 上按 C-c C-c,自动 tangle 所需的文件。
看到你这个 :post
我马上去查了一下有没有想对应的参数,可以在执行代码块之前运行做些预处理,可惜并没有找到
在 :var
里触发 tangle 是很取巧的操作,希望有更常规的做法,哪怕一个更适合放 (progn ...)
这段代码的地方。
顺便吐槽一下 :var n=
这种混搭写法:
#+BEGIN_SRC python :results output :var n=(+ 1 1)
print(n)
#+END_SRC
#+RESULTS:
: 2
难道不是应该 :var (n (+ 1 1))
吗?就像 snippet 这样。
要执行的语句不必放在 :var
中的变量后,
在任意以冒号开头名称(如 :name
)之后都行, C-c C-c
时 parse 阶段会先执行第一个带括号的语句,后面的语句会忽略。
代码块头部加上以下就行 :pref (your-org-babel-tangle "my_hello")
。
:var (n (+ 1 1))
中 n
也会被当作函数执行,而不是变量。
若想省去写函数名, 可以自定义属性名, 后接要 tangle 的代码块名。
如 :pref my_hello my_hello_2
,
然后用 (cdr (assq :pref (nth 2 (org-babel-get-src-block-info))))
可以获取这个参数值, 但多个参数时会以整个字符串返回,中间的空格未被去掉,
需要自行处理。而当你调用 org-babel-get-src-block-info
时,会多
parse 一次,有副作用时可能会产生奇怪的结果。
这里写了一个简单 parse 代码块头部的函数,输入符号,返回字符串列表:
(defun org-babel-parse-current-header (arg)
"Return the values of ARG from current code block."
(if-let ((src-params (org-element-property :parameters (org-element-context))))
(if-let ((args (member (symbol-name arg) (split-string src-params))))
(seq-take-while (lambda (str)
(/= ?: (string-to-char str)))
(cdr args)))))
(org-babel-parse-current-header :pref) ;; => ("my_hello" "my_hello_2")
(mapc 'your-org-babel-tangle (org-babel-parse-current-header :pref))
然后再写个函数 advice-add
:around
到 C-c C-c
的 org-babel-execute-src-block
或 org-babel-confirm-evaluate
, 后者有确认的开关 org-confirm-babel-evaluate
可以设置。
我吐槽的就是 n=(+ 1 1)
这种混搭写法,n=
右边来一串 elisp,给人一种错乱的感觉。如果只是简单赋一个数字 n=1
就更让人分不清到底右边是 elisp,还是底下 block 所使用的语言。
也可能 :var
这个关键字本身带有一定的误导性,如果写成 :let ((n (+ 1 1)))
,应该不会产生混淆。
既然都自己 perse 头部了,何不干脆 #+TANGLE: my_hello
呢:
#+NAME: my_hello
#+BEGIN_SRC emacs-lisp :tangle /tmp/hello.el
(message "Hello")
#+END_SRC
#+TANGLE: my_hello
#+BEGIN_SRC sh :results output
cat /tmp/hello.el
rm -f /tmp/hello.el
#+END_SRC
这样
#+HEADER: :pref (your-org-babel-tangle "my_hello")
#+BEGIN_SRC sh :results output
cat /tmp/hello.el
rm -f /tmp/hello.el
#+END_SRC
或者这样
#+BEGIN_SRC sh :results output :pref (your-org-babel-tangle "my_hello")
cat /tmp/hello.el
rm -f /tmp/hello.el
#+END_SRC
注:只会执行第一条语句,后面的忽略。下面这样 func2 和 func4 都不会执行。
#+BEGIN_SRC sh :results output :pref (func1 arg1) (func2 arg2) :blah (func3 arg3) (func4 arg4)
cat /tmp/hello.el
rm -f /tmp/hello.el
#+END_SRC