shell - elisp 解析异步 shell 命令的输出

标签 shell emacs clojure elisp cider

我有一个简单的 elisp 交互函数,用于启动 Clojure repl。

(defun boot-repl ()
  (interactive)
  (shell-command "boot repl wait &"))

它打开一个 *Async Sh​​ell Command* 缓冲区,稍后会出现以下文本:

nREPL server started on port 59795 on host 127.0.0.1 - nrepl://127.0.0.1:59795 Implicit target dir is deprecated, please use the target task instead. Set BOOT_EMIT_TARGET=no to disable implicit target dir.

我想监视此命令的输出以便能够解析端口(本例中为“59795”)。 即使只是第一行(在没有警告的情况下)也可以。

这样我就可以使用另一个命令连接到等待我的 Clojure REPL。

我不能使用 shell-command-to-string 因为命令不会返回并且它会永远阻止 emacs(boot repl wait 应该持续我的整个编程 session ,可能更多)。

使用 cider 可能很容易做到也有,不过没找到。

那么,如何在 Elisp 中解析异步 bash 命令的结果? 或者,我如何设置 Cider 来为我启动此 REPL 并连接到它?

最佳答案

要直接回答这个问题,您绝对可以使用 start-processset-process-filter 解析异步 shell 命令的输出:

(let ((proc (start-process "find" "find" "find" 
                  (expand-file-name "~") "-name" "*el")))
     (set-process-filter proc (lambda (proc line)
                    (message "process output: %s" line))))

( Docs for filter function )

但是请注意,上面的line不一定是一行,可能包括多行或断行。只要进程或 emacs 决定刷新某些输出,就会调用您的过滤器:

... /home/user/gopath/src/github.com/gongo/json-reformat/test/json-reformat-test.el /home/user/gopath/src/github.com/gongo/json-reformat/test/test- 处理输出:helper.el

在您的情况下,这可能意味着您的端口号可能被分成两个单独的进程过滤器调用。

为了解决这个问题,我们可以引入一个行缓冲和行分割包装器,它会为每个进程输出行调用您的过滤器:

(defun process-filter-line-buffer (real-filter)
 (let ((cum-string-sym (gensym "proc-filter-buff"))
       (newline (string-to-char "\n"))
       (string-indexof (lambda (string char start)
             (loop for i from start below (length string)
                   thereis (and (eq char (aref string i))
                        i)))))

   (set cum-string-sym "")
   `(lambda (proc string)
      (setf string (concat ,cum-string-sym string))
      (let ((start 0) new-start)
    (while (setf new-start
            (funcall ,string-indexof string ,newline start))

      ;;does not include newline
      (funcall ,real-filter proc (substring string start new-start))

      (setf start (1+ new-start)));;past newline

    (setf ,cum-string-sym (substring string start))))))

然后,您可以安全地期望您的行是完整的:

(let* ((test-output "\nREPL server started on port 59795 on host 127.0.0.1 - \nrepl://127.0.0.1:59795 Implicit target dir is deprecated, please use the target task instead. Set BOOT_EMIT_TARGET=no to disable implicit target dir.")
     (proc (start-process "echo-test" "echo-test" "echo" test-output)))
 (set-process-filter proc (process-filter-line-buffer
               (lambda (proc line)
                 (when (string-match
                    "REPL server started on port \\([0-9]+\\)"
                    line)
                   (let ((port (match-string 1 line)))
                 ;;take whatever action here with PORT
                 (message "port found: %s" port)))))))

最后,我对苹果酒不熟悉,但这种底层工作可能属于低级模式,可能已经解决了。

关于shell - elisp 解析异步 shell 命令的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35559591/

相关文章:

php - 如何将表从一个mysql数据库复制到另一个mysql数据库

Bash Shell 参数扩展 ${ }

bash 脚本重复数据删除

Emacs - 奇怪的空白

emacs - 如何在 emacs org-mode 中将函数限制为子树?

clojure - 如何使用 ClojureScript 和 Figwheel 与后端通信?

加载多个 FTP 文件的 Shell 脚本

没有 paredit 的 Emacs Clojure 模式

deployment - 构建和部署 Clojure 应用程序的最佳实践 : good tutorials?

macros - 使用宏生成函数(或其他宏)时避免符号捕获