elisp анализирует вывод асинхронной команды оболочки

У меня есть простая elisp интерактивная функция, которую я использую для запуска реплики Clojure.

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

Он открывает буфер *Async Shell Command*, и через некоторое время появляется следующий текст:

Сервер nREPL запущен на порту 59795 на хосте 127.0.0.1 — nrepl://127.0.0.1:59795 Неявный целевой каталог устарел, вместо этого используйте целевую задачу. Установите BOOT_EMIT_TARGET=no, чтобы отключить неявный целевой каталог.

Я хотел бы отслеживать вывод этой команды, чтобы иметь возможность анализировать порт (в данном примере — «59795»). Даже первая строка (в случае без предупреждений) будет в порядке.

Таким образом, я мог бы использовать другую команду для подключения к ожидающему меня Clojure REPL.

Я не могу использовать shell-command-to-string, так как команда не возвращает значение и блокирует emacs навсегда (boot repl wait должен длиться весь мой сеанс программирования, а возможно, и дольше).

Может быть что-то легко сделать и с cider, но я этого не нашел.

Итак, как мне проанализировать результат асинхронной команды bash в Elisp? В качестве альтернативы, как я могу настроить Cider для запуска этого REPL для себя и подключения к нему?


person nha    schedule 22.02.2016    source источник


Ответы (3)


Чтобы ответить на вопрос напрямую, вы определенно можете проанализировать вывод асинхронной команды оболочки, используя 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)))))))

Наконец, я не знаком с сидром, но такая низкоуровневая работа, вероятно, относится к низкоуровневому режиму и, вероятно, уже решена.

person ealfonso    schedule 24.04.2016

shell-command

позволяет называть необязательные буферы вывода и ошибок. Затем ошибка должна появиться внутри последнего и больше не загромождать вывод.

person Andreas Röhler    schedule 22.02.2016

Лучший ответ на другой, который я предоставил, - просто использовать сидр, как вы предложили:

(progn
     (package-refresh-contents)
     (package-install 'cider)
     (cider-jack-in))
person ealfonso    schedule 18.05.2016