elisp parse output of asynchronous shell command elisp parse output of asynchronous shell command shell shell

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))))

(Docs for filter function)

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.


shell-command

allows to name optional output- and error-buffers. Than the error should appear inside the latter and not clutter the output any more.


A better answer to the other one I provided is to simply use cider as you suggested:

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