elisp parse output of asynchronous shell command
To answer the question directly, you can definitely parse the output of an asyncronous shell command, using start-process
and 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))))
However, note that line
above is not necessarily a line, and may include multiple lines or broken lines. Your filter is called whenever the process or emacs decides to flush some ouput:
... /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
In your case, this could mean that your port number might be broken into two separate process-filter calls.
To fix this, we can introduce a line-buffering and line-splitting wrapper, which calls your filter for each process output line:
(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))))))
Then, you can safely expect your lines to be whole:
(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)))))))
Finally, I'm not familiar with cider but this kind of low-level work probably belongs in an inferior-type mode and has probably already been solved.