Run commands in Emacs asynchronously, but output the output step by step

I have a utility function:

(defun execute-in-buffer (command-with-args buffer) "Execute a string COMMAND-WITH-ARGS representing a shell command with arguments, inserting the results in BUFFER." (switch-to-buffer buffer) (insert (format ">>> %s\n" command-with-args)) (let* ((command-args-list (s-split " " command-with-args)) (command (car command-args-list)) (args (cdr command-args-list))) (apply 'call-process command nil buffer t args))) 

This allows me to do things like (execute-in-buffer "ls /" (get-buffer-create "*my-output*") . However, it is not very suitable for slow commands. If I call a series of slow commands, I I do not get any output to the very end:

 (let ((buf (get-buffer-create "*my-output"))) (execute-in-buffer "sleep 10" buf) (execute-in-buffer "ls /" buf)) 

I want to be able to call synchronously, so the next command only starts after the previous one is completed. However, I want to see the output from my commands when they run. How can I do it?

(The sample code is just for showing, I'm glad to leave it in favor of something else.)

+4
source share
2 answers

Using synchronous processes

If you want to adhere to synchronous processes (for example, using call-process ), you need to call (redisplay) after each call to execute-in-buffer to display, which will be updated, and the output should be visible (see also this question for more detailed information). However, the output of each command will not be displayed until the process is completed, and emacs will hang during the execution of external processes.

Using asynchronous processes

Using asynchronous processes is a bit more complicated, but avoids Emacs freezing during command execution, which also solves the problem of re-rendering. The hard part here is to consistently connect all the teams. Here is some elisp that should do the trick:

 (defun execute-commands (buffer &rest commands) "Execute a list of shell commands sequentially" (with-current-buffer buffer (set (make-local-variable 'commands-list) commands) (start-next-command))) (defun start-next-command () "Run the first command in the list" (if (null commands-list) (insert "\nDone.") (let ((command (car commands-list))) (setq commands-list (cdr commands-list)) (insert (format ">>> %s\n" command)) (let ((process (start-process-shell-command command (current-buffer) command))) (set-process-sentinel process 'sentinel))))) (defun sentinel (pe) "After a process exited, call `start-next-command' again" (let ((buffer (process-buffer p))) (when (not (null buffer)) (with-current-buffer buffer ;(insert (format "Command `%s' %s" pe) ) (start-next-command))))) ;; Example use (with-current-buffer (get-buffer-create "*output*") (erase-buffer)) (execute-commands "*output*" "echo 1" "sleep 1" "echo 2; sleep 1; echo 3" "ls /") 
+7
source

This works for me:

 (async-shell-command "echo 1; sleep 10; echo 2; sleep 10; ls /" "*abcd*") 

Can it be adapted to do what you need?

+5
source

Source: https://habr.com/ru/post/1483367/


All Articles