Emacs Lisp 中 Process 相關的玩意

這幾天在寫 hexo.el 遇到一些 Emacs Lisp 中 process 相關的處理方式,做個簡單的整理筆記(也順便試用 hexo.el )。不過有些因為目前我沒用過/用不到,所以也懶得去研究其中差異,不要打我。

Process

start-process (Async)

(start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS)

這是 Emacs Lisp 中最低階的 process 呼叫方法之一,他 stdout 輸出的 BUFFER (fundamental-mode) 連 ANSI color 都沒有(所以會看到一堆亂七八糟的 ANSI color 控制字元),要自己使用 set-process-filter 來自訂 Buffer 的 IO。

設定不出來的 filter…

看一下 set-process-filter 的文件:

1
2
3
4
5
6
7
8
9
10
(set-process-filter PROCESS FILTER)

Give PROCESS the filter function FILTER; nil means default.
A value of t means stop accepting output from the process.

When a process has a non-default filter, its buffer is not used for output.
Instead, each time it does output, the entire string of output is
passed to the filter.

The filter gets two arguments: the process and the string of output.

看起來自訂 filter 應該很容易,所以我這樣寫:

1
2
3
(set-process-filter my-process (lambda (process string)
(with-current-buffer (process-buffer process)
(insert (ansi-color-apply string)))))

但很奇怪,雖然 ANSI color 控制字元經過實驗確實被 ansi-color-apply 給消掉了,但不管怎麼試顏色(text-properties)仍舊就是出不來。這裡有待高人指點。

call-process (Sync)

(call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS)

總之我是把這玩意看作是 start-process 的 sync 版本。

start-process-shell-command (Async)

(start-process-shell-command NAME BUFFER COMMAND)

這玩意基本上跟 start-process 一樣,只是吃的 input 變成 shell command string 而已。如果你只是要開個 process 做些事情、不用去讀他的 output 的話,用這個沒問題。但如果你還需要漂亮的輸出…還是用 shell-command 吧。

call-process-shell-command (Sync)

(call-process-shell-command COMMAND &optional INFILE BUFFER DISPLAY)

應該只是 start-process-shell-command 的 sync 版本(好隨便)。

Shell command

shell-command (Sync)

(shell-command COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER)

這玩意很容易使用,看一下上面這兩行你大概就知道這是啥了。而且這玩意會自動幫你 render ANSI color 成 propertized text(用人類的話來說,就是你可以在 OUTPUT-BUFFER 中看到字體有顏色),真是太棒了。

async-shell-command (Async)

(async-shell-command COMMAND &optional OUTPUT-BUFFER ERROR-BUFFER)

shell-command 幾乎一樣,只是會自動幫你在 COMMAND 的結尾加上 &

萬一執行的指令需要輸入東西(例如 git push 可能會叫你輸入密碼)怎麼辦?

利用前面提過的 set-process-filter

1
2
3
4
5
(let ((process-buffer-name "*test-process*")
(process-object nil))
(async-shell-command "echo hello" process-buffer-name)
(setq process-object (get-buffer-process process-buffer-name))
(set-process-filter process-object 'comint-output-filter))

comint-output-filter 很方便,你不用自己浪費生命寫 filter,這個 filter 會自動幫你抓是否有 prompt 之類的東西。

shell-command-to-string (Sync)

(shell-command-to-string COMMAND)

這玩意本質跟 shell-command 一樣,只是用途就如名稱那樣是「執行完 shell command 後,return stdout 為字串」而不是另外開一個 buffer 來慢慢顯示 stdout。所以他只有 Synchronous。