emacs - 如何在elisp中使用过滤器执行sudo命令

标签 emacs lisp elisp

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

根据我的理解,在详尽的 RTFM 之后,我可以:

  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 专家,所以我怀疑我犯了一些愚蠢的错误。我对 *Messages* 缓冲区中的 "(lambda (proc ..." 也很好奇。

感谢任何建议、批评或提示。谢谢!

最佳答案

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

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

(message "Got output: %s" output)

(callback ip) 将无法工作,原因有二:

  • Emacs Lisp 对函数和变量有单独的命名空间,所以 (callback ip) 将调用 function callback(不存在),而您需要 (funcall callback ip) 来调用存储在变量 callback 中的函数。
  • 一旦你超过了那个,因为 Emacs Lisp 默认使用动态作用域,当你的 lambda 函数被调用时,callback 的绑定(bind)已经消失了。

    在 Emacs 24 中,您可以将 lexical-binding 设置为 t,上面的代码应该可以工作。在任何情况下,您都可以显式使用 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))))
    

    这个宏使用黑魔法在 lambda 函数中保留 callback 的值。

关于emacs - 如何在elisp中使用过滤器执行sudo命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16612529/

相关文章:

emacs - 如何在 Emacs-LISP 中将一个(非交互)函数变成一个命令(交互函数)?

python - 将输入发送到劣质 python 进程时 Mac 变慢

lisp - 如何让 defun 接受一个列表作为它的参数

types - Elisp 原子类型分类

emacs - 如何 `' load-file'` 当前缓冲区的关联文件?

Emacs close paren 在编辑clojure 代码时跳转到已经存在的close-paren

lisp - 遍历列表并附加到新列表

sum - 基本 Lisp 函数 - 偶数和减去奇数和

linux - 如何调试导致 "emacs24.4 --daemon"不返回shell的init文件?

json - Elisp - 来自 API 响应的 json-read-from-string