Search code examples
emacsbackground-processorg-modefreeze

Emacs org-mode - How to run shell scripts with backgrounded processes without hanging Emacs


Add this to an emacs .org file:

#+BEGIN_SRC sh :results verbatim
  #!/bin/bash

  exec 2>&1  # <-- Because Emacs treats stderr output as an error and doesn't show it in the RESULT

  echo before 

  # This nohup should just run in the background and continue to exit
  # the script, but emacs hangs and waits on it anyhow:
  nohup sleep 10 &

  # Failed attempts at working around the hang are:
  #   setsid nohup sleep 10 &
  #   nohup sleep 10 </dev/null & 

  # Do see /tmp/ps.out being updated here so the hang is in Emacs:
  ps -ef --forest --cols=10000 >/tmp/ps.out

  echo after 

  exit 0
#+END_SRC

Move the point (cursor) into the BEGIN_SRC block and evaluate it using C-c C-c (which is bound to org-ctrl-c-ctrl-c).

Watch what happens. Emacs sits there and hangs. What I want it to do is run that command (sleep 10 in this trivial example) and continue on.

Somehow Emacs is trying to wait on all subprocesses that the script creates and hangs there. I have to hit C-g to get control back.

The use case for this is that I want to invoke some GUI application (xterm, etc.) and have it run in the background but give control immediately back to Emacs.

How can I do that? See my failed attempts above.

EDIT: I isolated the issue to the bare minimum Emacs Lisp code. Evaluate the following inside your *scratch* (Lisp Interactive) buffer and see that it hangs for the 3 seconds:

(let ((shell-file-name "/bin/bash")
      (input-file "/tmp/tmpscript.sh")
      (error-file "/tmp/tmperror")
      (shell-command-switch "-c")
      (command "sh")
      exit-status)
  (with-temp-file input-file
    (insert "#!/bin/bash") (newline)
    (insert "nohup sleep 3 &") (newline)
    (insert "exit 0") (newline))
  (setq exit-status
        (apply 'call-process "/bin/bash"
                      input-file
                      (list t error-file)
                      nil
                      (list "-c" "sh"))))

Change the sleep 3 to something like sleep 3000 and it will hang for 3000 seconds until you kill it with C-g.

My emacs version reports:

GNU Emacs 24.4.50.1 (x86_64-unknown-linux-gnu, GTK+ Version 3.4.2) of 2014-09-14 on hungover

Solution

  • I use ob-async from MELPA to enable asynchronous executions.

    .emacs:

    (require 'ob-async)
    

    .org:

    #+BEGIN_SRC sh :async
    sleep 10 && echo 'Done!'
    #+END_SRC