tags:

views:

101

answers:

2

If I execute a shell command asynchronously in emacs lisp like so:

(shell-command "mycommand &")

Is there a way to wait for the command to generate output before continuing? For my current application, it is probably sufficient to wait until the command generates any output at all, but ideally I'd like to capture the output for additional processing. Is this possible?

+1  A: 

You should use comint-output-filter-functions variable that contains function to call after output is inserted into the buffer.

For example, you can do :

(add-hook 'comint-output-filter-functions '(lambda (txt) (message "hello")))

N.B. : From Emacs 23.2, you have the new command async-shell-command, bound globally to M-&. This executes your command asynchronously without requiring an ampersand. The output of your command is sent to the buffer *Async Shell Command*.

Jérôme Radix
I'm still on 23.1, so I don't have this command, but I get the same effect with shell-command and the ampersand. However, I need some sort of mechanism to wait until (or call back when) the output appears in the buffer.
Mike K
I've updated the answer to do what you need.
Jérôme Radix
At first I couldn't get the hook function to run. FWIW, I running 23.1.1 under Windows 7. However, your reference to comint-output-filter-functions led me to the following link:http://www.squidoo.com/emacs-comintThat example uses the hook in conjunction with make-comint to spawn the process which works for me.
Mike K
+2  A: 

Perhaps you need to register a Process Filter to give you the callback timing you need? See 37.9 Receiving Output from Processes in the Elisp manual (I see this in my copy for Emacs 22.3).

Here is an example of running a callback when you get the first process output and also storing it into an "associated buffer". Copy it to your *scratch* buffer and eval-region it, but make sure to split-window and show the *Messages* buffer visible so that you can see what's going on.

;; this is emacs lisp (and a comment line)
(defvar my-callback-got-some-already nil)

(defun my-callback ()
  (message "callback ran at   %s" (current-time-string)))

(defun my-filter-waits-for-first-time-input (proc string)
  (unless my-callback-got-some-already
    (setq my-callback-got-some-already t)
    ;; do your one-time thing
    (my-callback))
  ;; insert into the associated buffer as if no process filter was
  ;; registered
  (with-current-buffer (process-buffer proc)
    (let ((moving (= (point) (process-mark proc))))
      (save-excursion
        ;; Insert the text, advancing the process marker.
        (goto-char (process-mark proc))
        (insert string)
        (set-marker (process-mark proc) (point)))
      (if moving (goto-char (process-mark proc))))))

(defun async-callback-test-harness ()
  (interactive)
  (let ((process-handle "async-callback-test")
        (associated-process-buffer "*async-callback-test*")
        (command "ls")
        (busy-loop-var ""))
  (setq my-callback-got-some-already nil)
  (message "start test        %s" (current-time-string))
  (start-process process-handle associated-process-buffer command)
  ;; Supposedly async but Emacs doesn't get the input until
  ;; "Emacs is waiting" so the following set-process-filter
  ;; can be registered in time.
  ;; To prove the point, make emacs busy loop to show that the
  ;; emacs doesn't drop its input and 
  ;; the callback will get the unskipped input.
  (switch-to-buffer associated-process-buffer)
  (dotimes (k 2000) ; about two seconds on my machine
    (setq busy-loop-var (concat busy-loop-var "busy looping...")))
  (message "done busy waiting %s" (current-time-string))
  (set-process-filter (get-process process-handle)
                      'my-filter-waits-for-first-time-input)
  nil))

;; run it!
(async-callback-test-harness)
piyo
This looked promising, but I could never get the callback to be called. See my comment on Jérôme Radix's answer for more details.
Mike K