我想使用elisp执行特权命令,然后在看到某些输出时使用过滤器进行进一步处理。
我的理解是,在详尽的RTFM之后,我可以:
default-directory
设置为以“/ sudo :: /”开头的路径。start-file-process
以启动将在sudo下运行的流程。这是我写的一个尝试这样做的函数:
(defun start-vpn (config-file password-file callback)
"Starts the VPN connection, waits for a DHCP address,
then invokes callback with the address it got."
(let* ((default-directory "/sudo::/tmp")
(buff (get-buffer-create "*openvpn*"))
(proc (start-file-process "openvpn" "*openvpn*"
"/usr/local/sbin/openvpn"
(concat "--config " config-file)
(concat "--auth-user-pass " password-file))))
(set-process-filter proc
(lambda (proc output)
(message "Got output: " output)
(let ((ip (get-vpn-ip-address output)))
(when ip
(callback ip)))))))
当我运行它时,我在*Messages*
缓冲区中看到以下输出:
start-vpn
Tramp: Opening connection for root@localhost using sudo...
Tramp: Sending command `exec sudo -u root -s -H -p Password:'
Tramp: Waiting for prompts from remote shell
Tramp: Sending command `exec sudo -u root -s -H -p Password:'
Tramp: Found remote shell prompt on `localhost'
Tramp: Opening connection for root@localhost using sudo...done
(lambda (proc output) (message "Got output: " output) (let ((ip ...)) (if ip (progn ...))))
Got output:
...并且函数创建的*openvpn*
缓冲区中没有输出。
我不是elisp的专家,所以我怀疑我犯的是一些愚蠢的错误。我对"(lambda (proc ..."
缓冲区中的*Messages*
非常好奇。
任何建议,批评或提示都表示赞赏。谢谢!
答案 0 :(得分:3)
首先,在消息缓冲区中看到(lambda ...
的原因是set-process-filter
返回过滤器函数,因此start-vpn
也会这样做。
您的message
调用需要包含实际显示输出的格式说明符:
(message "Got output: %s" output)
并且(callback ip)
不会有两个原因:
(callback ip)
将调用函数 callback
(不存在),而您需要{{1}调用存储在变量 (funcall callback ip)
中的函数。一旦你过去了,因为默认情况下Emacs Lisp使用动态范围,当你的lambda函数被调用时,callback
的绑定已经消失。
在Emacs 24中,您可以将callback
设置为lexical-binding
,上面的代码应该有效。无论如何,您可以明确使用t
宏:
lexical-let
此宏使用黑魔术来保留lambda函数中(lexical-let ((callback callback))
(lambda (proc output)
(message "Got output: " output)
(let ((ip (get-vpn-ip-address output)))
(when ip
(funcall callback ip))))
的值。