如何避免在Emacs中弹出*Async Shell Command*缓冲区?

21

我的~/.emacs文件包含了在Ubuntu 12.10下使用Emacs 24打开特定文件的以下设置:

(setq dired-guess-shell-alist-user
      '(("\\.pdf\\'" "okular ? &")
    ("\\.djvu\\'" "okular ? &")
        ("\\.mp3\\'" "vlc ? &")
    ("\\.mp4\\'" "vlc ? &")
    ))

当我在dired-mode中导航到一个.pdf文件并按下!时,它会在Okular中打开.pdf文件,但dired缓冲区被分成两个部分,第二个部分现在是一个无用的包含内容的*Async Shell Command*缓冲区。

okular(25393)/kdecore (KConfigSkeleton) KCoreConfigSkeleton::writeConfig:
okular(25393)/kdecore (KConfigSkeleton) KCoreConfigSkeleton::writeConfig:
okular(25393)/kdecore (KConfigSkeleton) KCoreConfigSkeleton::writeConfig:
okular(25393)/kdecore (KConfigSkeleton) KCoreConfigSkeleton::writeConfig:
我该如何防止该缓冲区被打开?(除非发生错误并且此信息有用)。我在这里这里找到了相关问题,但它们似乎处理特定的异步执行命令,而不是一般情况下的*Async Shell Command*。(如果可能的话,我想改变异步进程的行为,而不只是针对某些文件类型)

请查看.../lisp/simple.el源代码中的实际函数--即defun shell-commanddefun async-shell-command。您甚至可以创建自己的自定义函数和/或使用defalias。在使用start-process时,第二个参数是输出缓冲区名称--使用nil作为第二个参数可以防止创建输出缓冲区。您可以将set-process-sentinelstart-process结合使用。 - lawlist
async-shell-command 的文档字符串指出:在 Elisp 中,直接调用 start-process 通常更好,因为它提供了更多的控制,并且不强制使用 shell(需要引用参数)。 - lawlist
8个回答

26

在这里找到了这个

(call-process-shell-command "okular&" nil 0)

没问题。没有标准错误的胡言乱语。


1
实际上,这是唯一一个确实完全有效的答案:没有日志消息和奇怪的弹出窗口。 - Manoel Vilela
有人能详细说明一下吗?我应该把这个函数放在dired-guess-shell-alist-user里面吗?怎么做呢?我刚试了一下:(add-to-list 'dired-guess-shell-alist-user '("\\.pdf$" (call-process-shell-command "evince" nil 0))),但它不起作用——它打开了evince,但没有打开指定的文件。 - kotchwane

14

这个问题是在2012年提出的,而在我写作时,最近的答案是2015年的。现在,到了2017年,我可以说答案很简单:

(add-to-list 'display-buffer-alist
  (cons "\\*Async Shell Command\\*.*" (cons #'display-buffer-no-window nil)))

2
这仍然会创建一个缓冲区,但至少它不会弹出并占据屏幕的一半。我坚持使用这个,因为它是最简单的解决方案。 - Nikaoto

6
我借鉴了用户1404316的答案,但这里还有另一种通用的方法来实现所需的结果。
(defun async-shell-command-no-window
    (command)
  (interactive)
  (let
      ((display-buffer-alist
        (list
         (cons
          "\\*Async Shell Command\\*.*"
          (cons #'display-buffer-no-window nil)))))
    (async-shell-command
     command)))

这是最简单的答案!仅针对特定命令避免缓冲区。用它来编写创建TAGS文件的命令 (defun mktags-here () (interactive) ... (async-shell-command (message (format "ctags -e -R .")))) - Andreas Spindler
对我来说,使用 call-process-shell-command 命令并在命令后添加 & 是最简单的。 - RichieHH

1
很遗憾,没有好的方法可以避免这个缓冲区,因为它是由“shell-command”函数直接调用的(“async-shell-command”只是一个包装器)。
因此,更好的方法是使用“start-process”替换“async-shell-command”。您应该使用“set-process-sentinel”启动进程以检测进程发出“退出信号”的时刻。然后杀死进程。

start-process 有根本的不同,因为您无法传递在当前目录上下文中执行的 SHELL 命令。 - RichieHH

1

我不确定是否适用于所有异步进程,但对于通过async-shell-command执行的任何操作,这应该是可行的:

    (defadvice async-shell-command (around hide-async-windows activate)
       (save-window-excursion
          ad-do-it))

但是这样做会强制所有使用async-shell-command的缓冲区都丢失。我认为这可能有些过头了;肯定会有一些情况需要查看输出结果。将建议更改为添加一个参数,以指定是否执行save-window-excursion应该不会太难,但那么为什么要首先使用建议呢?最好使用一个简单的包装函数。 - tripleee
我确实意识到 OP 特别要求修复 async-shell-command,但是应该强调搞乱它的注意事项。 - tripleee
这对我没有用。我仍然得到异步Shell命令缓冲区。 - Marius Hofert
这是因为 async-shell-command 调用 shell-command 来完成其操作。 - lawlist

0

我用以下方法解决了这个问题:

;list of programs, corresponding to extensions
(setq alist-programs
      '(("pdf" ."okular")
        ("djvu" . "okular")
        ("mp3" . "xmms")))

(defun my-run-async-command (command file)
  "Run a command COMMAND on the file asynchronously.
   No buffers are created"
  (interactive
   (let ((file (car (dired-get-marked-files t current-prefix-arg))))
     (list
      ;last element of alist-programs, contains COMMAND
      (cdr
       (assoc
        (file-name-extension file)
        alist-programs))
      file)))
  ;begin of function body
  (if command ;command if not nil?
      (start-process "command" nil command file)
    )
)

;attach function to <f2> key
(add-hook 'dired-mode-hook
      (lambda ()
    (define-key dired-mode-map (kbd "<f2>") 'my-run-async-command)))

0

如果他正在运行特定路径上的不同程序,则使用start-process的建议是可以的。但是,如果您想在特定目录(例如您的项目目录)中运行一些shell命令,那么只需压制弹出窗口-您经常需要缓冲区,但只是不想让它跳到您的面前。例如,我运行一个Web服务器,我想看到输出,但现在不想看...

  (use-package php-mode
    :config
    (add-to-list 'display-buffer-alist
    (cons "\\*Symfony Web Server\\*.*" (cons #'display-buffer-no-window nil)))
    (defun php-mode-webserver-hook ()
      (if (projectile-project-root) (let ((default-directory (projectile-project-root)))
        (unless (get-buffer "*Symfony Web Server*" )
          (async-shell-command "bin/console server:run" "*Symfony Web Server*")))))
    :hook (php-mode . php-mode-webserver-hook))

0
稍微复杂一些的咒语可以让你得到想要的结果。只需使用如下 shell 命令:(okular ? >& /dev/null &)
我没有用 okular 进行过测试,但是我可以使用 M-! ((echo foo; sleep 10; echo bar) >& /dev/null &),Emacs会立即返回而不创建新的缓冲区。

不错。然而,Async Shell Command 缓冲区仍会弹出(现在只是空的)。如果我尝试使用您的 M-! ... 命令,我会得到“shell command succeeded with no output”。 - Marius Hofert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接