elisp 解析异步 shell 命令的输出
elisp parse output of asynchronous shell command
我有一个简单的 elisp
交互函数,用于启动 Clojure repl。
(defun boot-repl ()
(interactive)
(shell-command "boot repl wait &"))
它打开一个 *Async Shell 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
,因为该命令不会 return 并且它会永远阻止 emacs(boot repl wait
应该持续我的整个编程会话,可能更多)。
也许 cider
也有一些简单的操作,但我还没有找到。
那么,如何在 Elisp 中解析异步 bash 命令的结果?
或者,我如何设置 Cider 来为我启动此 REPL 并连接到它?
shell-command
允许命名可选的输出和错误缓冲区。错误应该出现在后者内部并且不再使输出混乱。
要直接回答这个问题,您绝对可以使用 start-process
和 set-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))))
但是请注意,上面的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)))))))
最后,我对苹果酒不熟悉,这种底层工作可能属于低级模式,应该已经解决了。
对于我提供的另一个更好的答案是按照您的建议简单地使用苹果酒:
(progn
(package-refresh-contents)
(package-install 'cider)
(cider-jack-in))
我有一个简单的 elisp
交互函数,用于启动 Clojure repl。
(defun boot-repl ()
(interactive)
(shell-command "boot repl wait &"))
它打开一个 *Async Shell 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
,因为该命令不会 return 并且它会永远阻止 emacs(boot repl wait
应该持续我的整个编程会话,可能更多)。
也许 cider
也有一些简单的操作,但我还没有找到。
那么,如何在 Elisp 中解析异步 bash 命令的结果? 或者,我如何设置 Cider 来为我启动此 REPL 并连接到它?
shell-command
允许命名可选的输出和错误缓冲区。错误应该出现在后者内部并且不再使输出混乱。
要直接回答这个问题,您绝对可以使用 start-process
和 set-process-filter
:
(let ((proc (start-process "find" "find" "find"
(expand-file-name "~") "-name" "*el")))
(set-process-filter proc (lambda (proc line)
(message "process output: %s" line))))
但是请注意,上面的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)))))))
最后,我对苹果酒不熟悉,这种底层工作可能属于低级模式,应该已经解决了。
对于我提供的另一个更好的答案是按照您的建议简单地使用苹果酒:
(progn
(package-refresh-contents)
(package-install 'cider)
(cider-jack-in))