【问题标题】:Emacs org-mode - How to run shell scripts with backgrounded processes without hanging EmacsEmacs org-mode - 如何在不挂起 Emacs 的情况下使用后台进程运行 shell 脚本
【发布时间】:2015-08-05 14:26:52
【问题描述】:

将此添加到 emacs .org 文件中:

#+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

将点(光标)移动到 BEGIN_SRC 块中并使用 C-c C-c(绑定到 org-ctrl-c-ctrl-c)对其进行评估。

看看会发生什么。 Emacs 坐在那里挂起。我想要它做的是运行该命令(在这个简单的示例中为sleep 10)并继续。

不知何故,Emacs 试图等待脚本创建并挂在那里的所有子进程。我必须按 C-g 才能重新获得控制权。

用例是我想调用一些 GUI 应用程序(xterm 等)并让它在后台运行,但立即将控制权交还给 Emacs。

我该怎么做?请参阅上面的失败尝试。

编辑:我将这个问题隔离到了最低限度的 Emacs Lisp 代码中。在 *scratch* (Lisp Interactive) 缓冲区中评估以下内容,看看它是否挂起 3 秒:

(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"))))

sleep 3 更改为sleep 3000 之类的东西,它会挂起 3000 秒,直到你用 C-g 杀死它。

我的 emacs 版本报告:

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

【问题讨论】:

    标签: emacs background-process org-mode freeze


    【解决方案1】:

    我使用来自 MELPA 的 ob-async 来启用异步执行。

    .emacs:

    (require 'ob-async)
    

    .org:

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

    【讨论】:

    • 谢谢!仅供参考:自从我发布问题以来,我已经开始推出自己的解决方案,其工作方式类似于 ob-async,而是将块的输出写入文件,然后在大约 3 秒不活动后向我发送提醒在 Emacs 中。这使我可以运行多个后台“作业”,当我暂停时,Emacs 打开并移动到作业完成的确切时间点,从那里我可以简单地查找日志文件并查看它。这是强制性的,因为我的后台作业产生的日志非常大,如果我内联它们会使 Org 文件膨胀。
    【解决方案2】:

    这是我能想到的最接近的使用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
    

    或用于您的特定用途:

    (require 'async)
    
    (async-start
     (lambda ()
       (shell-command "xterm"))
     (lambda (result)
       (message-box "%s" result)))
    

    【讨论】:

    • 我不希望将整个脚本作为后台进程运行。我想用 Bash 语法表达应该启动哪些子进程(我的示例是实际脚本的精简版)。我将问题归结为最低限度的 Emacs Lisp 代码,并将其作为 EDIT 发布到我的原始帖子中。这表明这是 Emacs 中的 call-process 和 Bash 之间的某种交互。
    • 当然可以。调用进程是同步的,等待进程完成。gnu.org/software/emacs/manual/html_node/elisp/…。如果你不能用异步来做,可以看看 start-process.gnu.org/software/emacs/manual/html_node/elisp/…。你可以在这里找到一些灵感http://kitchingroup.cheme.cmu.edu/blog/2015/11/20/Asynchronously-running-python-blocks-in-org-mode/。
    【解决方案3】:

    我正在使用 emacs lisp start-process:

    例如,打开 libreoffice 计算表

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

    或在 xterm 的后台启动 java 服务器(在示例表格中)

    #+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
    

    【讨论】:

      【解决方案4】:

      我在 Mac 上。对于我的用例,我想使用链接。我在命令中添加了 &amp; 以获得 sh 链接。使用&amp;,进程在后台启动:

      [[shell:afplay '/path/to/20200523-20123124.m4a' &][sample link]]
      

      我添加了(setq org-confirm-shell-link-function nil) b/c 我有数百个小 m4a 文件要转录...这不安全,但现在没关系。

      运行链接(使用C-x C-o)会打开一个新的 org shell 输出窗口,但我只是忽略它。

      干杯! z

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-18
        • 1970-01-01
        • 1970-01-01
        • 2011-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-19
        相关资源
        最近更新 更多