Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 926
bgoodr Avatar asked Aug 05 '15 14:08

bgoodr


3 Answers

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
like image 194
schastar Avatar answered Nov 08 '22 11:11

schastar


I am using emacs lisp start-process:

For example, to open libreoffice calc sheet

#+BEGIN_SRC elisp :noweb yes :results output :eval no-export :exports none 
(start-process "server" "buf-server" "libreoffice"  "calc.ods")
#+END_SRC

or to launch a java server (in the example tabula) in the background in xterm

#+BEGIN_SRC elisp :noweb yes :results output :eval no-export :exports none 
(start-process "server" "buf-server" "xterm"  "-T" "TABULA" "-hold" "-e"  "bash" "-c" "java -Dfile.encoding=utf-8 -Xms256M -Xmx1024M -jar ~/java/tabula.jar")
#+END_SRC
like image 1
jarjuk Avatar answered Nov 08 '22 13:11

jarjuk


Here is the closest I can think of using https://github.com/jwiegley/emacs-async

    * Run shell command asynchronously

#+BEGIN_SRC sh :tangle some_cmd.sh :tangle-mode (identity #o755) :shebang #!/bin/bash
sleep 10
touch async-file
echo "done"
#+END_SRC

#+RESULTS:

#+BEGIN_SRC emacs-lisp
(require 'async)

(org-babel-tangle)

(async-start
 (lambda ()
   (shell-command (expand-file-name "some_cmd.sh")))
 (lambda (result)
   (message-box "%s" result)))

#+END_SRC

#+RESULTS:
: #<process emacs>

#+BEGIN_SRC sh
ls async-file
#+END_SRC

or for your particular use:

(require 'async)

(async-start
 (lambda ()
   (shell-command "xterm"))
 (lambda (result)
   (message-box "%s" result)))
like image 2
John Kitchin Avatar answered Nov 08 '22 11:11

John Kitchin