3

我想使用 elisp 执行特权命令,然后在看到某些输出时使用过滤器进行进一步处理。

我的理解是,在详尽的 RTFM'ing 之后,我可以:

  1. 设置default-directory为以“/sudo::/”开头的路径。
  2. 调用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*

任何建议、批评或提示表示赞赏。谢谢!

4

1 回答 1

3

首先,(lambda ...在消息缓冲区中看到的原因是set-process-filter返回过滤器函数,因此start-vpn也是如此。

您的message调用需要包含格式说明符才能实际显示输出:

(message "Got output: %s" output)

并且(callback ip)不会工作有两个原因:

  • Emacs Lisp 对函数和变量有单独的命名空间,所以(callback ip)会调用函数 callback(它不存在),而你需要(funcall callback ip)调用存储在变量 callback中的函数。
  • 一旦你过去了,因为 Emacs Lisp 默认使用动态范围,当你的 lambda 函数被调用时,绑定callback已经消失了。

    在 Emacs 24 中,您可以设置lexical-bindingt,上面的代码应该可以工作。在任何情况下,您都可以显式使用lexical-let宏:

    (lexical-let ((callback callback))
      (lambda (proc output)
        (message "Got output: " output)
        (let ((ip (get-vpn-ip-address output)))
          (when ip
            (funcall callback ip))))
    

    这个宏使用黑魔法来保存callbacklambda 函数内部的值。

于 2013-05-17T16:14:38.813 回答