4

I am trying to do the following:

  1. open a shell in emacs and cd into project folder
  2. Execute a mercurial pull from repository.
  3. "wait" until the pull finishes
  4. Communicate that the pull is finished to elisp command which fired up the shell command to begin with so it could carry on with compile command to build the tree.

is there a way/code that would let me know when the shell command has finished before starting a new elisp command from inside .emacs file?

SFbay007
  • 1,917
  • 1
  • 20
  • 39
  • Once Emacs starts an asynchronous process, there's really no way to tell when it's finished. To complicate things, if you're opening an interactive shell, the process isn't actually finished when `hg pull` finishes, it is still alive! In situations like this, most projects will just use two commands - one for steps 1 & 2, and another for step 4. ESS (http://ess.r-project.org/) does exactly this in a similar context. – Tyler Apr 11 '14 at 21:19
  • 2
    This is what I use -- `start-process` and `set-process-sentinel`: http://stackoverflow.com/a/18707182/2112489 The example is for latexmk, but it should work just fine for other stuff too. – lawlist Apr 12 '14 at 02:23

3 Answers3

3

I think you don't have to start a physical shell to do so. In your elisp you can use for example call-process-shell-command, and it will wait till the command you enter, say "cd directory ; hg pull" finishes.

To make a test, you can try (C-x C-e) in the *scratch* window:

(call-process-shell-command "cd directory ; ls" nil t)

It will show you the list of files of that directory before the cursor, and it will wait to execute the next command till the command finishes.

Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
  • Thanks for the response Diego. I wanted to start a shell in emacs so I can see the progress of the pull. Sometimes there are merge complications that require further action. So the shell command will handle the pulling. I need a way to wait until it finished so I can use the compile command to start the make process. I could use the make itself from inside the shell as well, but I like the features provided by the compile package were it shows errors during compilation with the option of jumping to the error directly. – SFbay007 Apr 11 '14 at 20:38
  • Diego, is there at least a way to actively forward the output of the process call into an emacs buffer? – SFbay007 Apr 11 '14 at 21:25
  • Yes, just read the documentation for `start-process`. Use it to start a process with output directed to a certain buffer, after it starts up, use `display-buffer` to show the output. `start-process` will return the process object. Set the process's sentinal function with `set-process-sentinal` (read the docs for sentinals) use the sentinal to execute elisp when the process finishes successfully. – Jordon Biondo Apr 12 '14 at 17:07
  • As Tyler comments above, you either start the process and "disconnect" the input, using any variant of `start-process` or `start-process-shell-command`, or you use any shell buffer and lose the information on when the process has finished (although you can, as in the shell buffer, control merge errors). Another thing that comes to my mind is that you could start the Mercurial process so that it fails if it finds conflicts. This way you could be informed of the error and treat the conflicts yourself. As conflicts tend to be scarce, this will work 90% of the time, and it won't require any action – Diego Sevilla Apr 12 '14 at 21:42
2

@Francesco previously answered a similar question at the following link: https://stackoverflow.com/a/18707182/2112489

I discovered that using start-process as a let-bound variable caused it to be run immediately, rather than waiting until the more desirable moment further on down in the function. I also discovered that set-process-sentinel prevents using let-bound variables previously defined. Rather than using let-bound variables or global variables, another option would be to use a buffer-local variable -- e.g., (defvar my-local-variable nil "This is my local variable.") (make-variable-buffer-local 'my-local-variable) -- and set it within the function -- e.g., (setq my-local-variable "hello-world"). Although most examples that I have seen factor out the sentinel, I like to include everything into just one function -- e.g., such as the following snippet demonstrates:

(set-process-sentinel 
  (start-process
    "my-process-name-one"
     "*OUTPUT-BUFFER*"
    "/path/to/executable"
    "argument-one"
    "argument-two"
    "argument-three")
  (lambda (p e) (when (= 0 (process-exit-status p))
    (set-process-sentinel 
      (start-process
        "my-process-name-two"
        nil ;; example of not using an output buffer
        "/path/to/executable"
        "argument-one"
        "argument-two"
        "argument-three")
      (lambda (p e) (when (= 0 (process-exit-status p))
        (set-process-sentinel 
          (start-process . . . ))))))))
Community
  • 1
  • 1
lawlist
  • 13,099
  • 3
  • 49
  • 158
  • lawlist, I couldn't figure out the purpose behind my-local-variable. Could you include it in your example above to see understand the effect? – SFbay007 Apr 14 '14 at 23:12
  • When too many global variables exist with values and common names for the variables, certain functions may fail if by chance they use the same variable -- e.g., `filename` and `file-name`. By defining the variables as buffer-local, it is `nil` everywhere except for that one buffer where it is being used, and it disappears once the buffer is closed. I would prefer to use let-bound variables, but that doesn't work well inside the `set-process-sentinel`. So the best option appears to be using buffer-local variables to shorten up the script writing and not keep repeating the same long values. – lawlist Apr 14 '14 at 23:19
  • 1
    Here is a link to an updated example that uses buffer-local variables instead of global or let-bound variables to shorten up the script writing: http://stackoverflow.com/q/18705774/2112489 and here is another example: http://stackoverflow.com/q/23039562/2112489 – lawlist Apr 14 '14 at 23:24
  • 1
    Just to clarify, you still need to *name* your variables appropriately (to avoid potential name-clashes with other variables), regardless of whether you are only assigning buffer-local values to them. – phils Apr 14 '14 at 23:50
  • lexical-let is another option – unhammer Dec 02 '15 at 11:46
  • Note that the test with `process-exit-status` is inappropriate if you want to start the next process when the previous has finished. For an instance `(process-exit-status p)` also returns 0 if `p` is stopped or woken up. The process might receive such signals from the outside of Emacs. Use `process-status` instead as I have mentioned [there](https://emacs.stackexchange.com/questions/47142/multiple-async-shell-command-commands-in-sequence#comment72555_47148). – Tobias Jan 15 '19 at 11:27
0
(defun async-proc-with-context ()
  "experiment! set process completion context using the plist"
  (let* ((cmd "ls -la")
         (ctx "it finished")
         (output-buffer (generate-new-buffer (format "*bufname*")))
         (proc (progn
                 (async-shell-command cmd output-buffer)
                 (get-buffer-process output-buffer))))
    (if (process-live-p proc)
        (progn
          (process-put proc 'jw-ctx ctx) ;; set the context
          (set-process-sentinel proc '(lambda (process signal)
                                        (when (memq (process-status process) '(exit signal))
                                          (message (process-get process 'jw-ctx)) ;; get the context
                                          (shell-command-sentinel process signal))))))))