【问题标题】:elisp parse output of asynchronous shell commandelisp 解析异步 shell 命令的输出
【发布时间】:2016-02-22 17:06:14
【问题描述】:

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

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

它会打开一个*Async Shell Command* 缓冲区,然后会出现以下文本:

nREPL 服务器在主机 127.0.0.1 上的端口 59795 上启动 - nrepl://127.0.0.1:59795 不推荐使用隐式目标目录,请使用 而是目标任务。设置 BOOT_EMIT_TARGET=no 以禁用隐式 目标目录。

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

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

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

cider 也可能有一些简单的操作,但我还没有找到。

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

【问题讨论】:

    标签: shell emacs clojure elisp cider


    【解决方案1】:

    要直接回答这个问题,您绝对可以使用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- process output: 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)))))))
    

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

    【讨论】:

      【解决方案2】:
      shell-command
      

      允许命名可选的输出和错误缓冲区。错误应该出现在后者内部并且不会再使输出混乱。

      【讨论】:

        【解决方案3】:

        对我提供的另一个更好的答案是按照您的建议简单地使用苹果酒:

        (progn
             (package-refresh-contents)
             (package-install 'cider)
             (cider-jack-in))
        

        【讨论】:

          猜你喜欢
          • 2012-08-18
          • 2019-01-06
          • 2014-05-12
          • 2011-01-23
          • 2011-06-28
          • 1970-01-01
          • 2018-10-25
          • 1970-01-01
          相关资源
          最近更新 更多