如何快速地切换到shell运行程序,并在结束后自动返回?

我想要运行一个c++程序,因为需要输入,所以不能后台执行。因此我需要暂时切换到shell里面去运行这个程序。 那么我可以在二者间快速切换吗?如下面这个流程:

按下快捷键切换到shell并自动编译运行程序 -> 手动输入数据 -> 程序运行完输出结果 -> 按下空格(或者其他键)自动返回

这套操作在vim中可以借助 ! 轻松完成,在emacs中可以做到吗?

貌似可以通过M-&实现 但是这个会弹出一个新的buffer,自动切换到这个bufferne?

你好像用的是spacemacs? 自带的有两个函数SPC ! (shell-command) and SPC ' (spacemacs/default-pop-shell)。基本能满足我的需求了。虽然我还是用iterm的多,支持比较全,bug比较少。

这个函数copy过来的你也可以考虑。

;; "http://xuchunyang.me/Opening-iTerm-From-an-Emacs-Buffer/"
(defuniterm-shell-command (command &optional prefix)
  "cd to `default-directory' then run COMMAND in iTerm.
With PREFIX, cd to project root."
  (interactive (list (read-shell-command
                      "iTerm Shell Command: ")
                     current-prefix-arg))
  (let* ((dir (if prefix (ztlevi/git-project-root)
                default-directory))
         ;; if COMMAND is empty, just change directory
         (cmd (format "cd %s ;%s" dir command)))
    (do-applescript
     (format
      "
  tell application \"iTerm2\"
       activate
       set _session to current session of current window
       tell _session
            set command to get the clipboard
            write text \"%s\"
       end tell
  end tell
  " cmd))))

这两个也是,切换的话直接Alt(command)-tab不也可以么。

(defun ztlevi/open-terminal-in-project-root ()
  (interactive)
  (setq-local projectile-project-root-path (if (ignore-errors (projectile-project-root))
                                               (projectile-project-root) "."))
  (cond
   ((string-equal system-type "darwin") (shell-command (concat "open -a iTerm " projectile-project-root-path)))
   ((string-equal system-type "gnu/linux")
    (let (
          (process-connection-type nil)
          (openFileProgram (if (file-exists-p "/usr/bin/konsole")
                               "/usr/bin/konsole"
                             "/usr/bin/gnome-terminal")))
      (start-process "" nil openFileProgram (projectile-project-root))))))

(defun ztlevi/open-terminal-in-current-dir ()
  (interactive)
  (cond
   ((string-equal system-type "darwin") (shell-command (concat "open -a iTerm .")))
   ((string-equal system-type "gnu/linux")
    (let (
          (process-connection-type nil)
          (openFileProgram (if (file-exists-p "/usr/bin/konsole")
                               "/usr/bin/konsole"
                             "/usr/bin/gnome-terminal")))
      (start-process "" nil openFileProgram ".")))))

shell里加个key或者命令,执行emacsclient -n之类的(我发现这个后面不加文件名不让执行……)

multi term? c-z fg?

能,但 Emacs 的 Terminal Emulator 体验不好,没法与 Vim 的相提并论,更没法跟真正的终端模拟器相比。

下面介绍一个用 Term 解决的思路,灵感来自于 Eshell 的 eshell-visual-commands

示例程序:

/* foo.c */
#include <stdio.h>

int main(void) {
  puts("Enter a char:");
  putchar(getchar());
  return 0;
}

在 Term 里运行一个 Shell 命令(如果只想运行一个程序,如 Vim,用 M-x term vim 也行):

(defun term-shell-command (command)
  (interactive
   (list (read-shell-command "Terminal Shell command: ")))
  (let ((buffer (generate-new-buffer "*Terminal Shell Command*")))
    (switch-to-buffer buffer)
    (term-mode)
    (term-exec buffer "bash" "bash" nil nil)
    (term-send-string (get-buffer-process buffer) (concat command "\n"))
    (term-char-mode)))

编译、运行这只示例程序:

M-x term-shell-command cc foo.c && ./a.out ; exit

至于「结束后自动返回」很容易实现,可参考 Eshell 的 eshell-destroy-buffer-when-process-dies

(假设 Emacs 运行在终端里)一个方法是:

M-: (suspend-emacs "cc foo.c && ./a.out ; read ; fg")

我写了一个这样的函数,差不多能解决我的问题了

(defun my/run ()
  (interactive)
  (let ((file (file-name-nondirectory buffer-file-name)))
	(async-shell-command
	 (concat "time ./" (file-name-sans-extension file)))
	(switch-to-buffer-other-window "*Async Shell Command*")
	)
  )

这个不错

我用的不是 Spacemacs 对于(spacemacs/default-pop-shell)我有点感兴趣

shell-pop这个包实现了

因为需要输入,所以不能后台执行

这个用 compile 的 comint mode 就可以了吧,方便快捷。https://vimeo.com/user87956344/review/282636894/1d0c4dd193

1 个赞

长见识了,我都不知道有这个东西!

You can try eshell-exec-visual. as below.

(setq eshell-destroy-buffer-when-process-dies t)
(eshell-exec-visual "top")			 
(eshell-exec-visual "/bin/sh" "-c" "{ ls -l ; sleep 2 ; }")

感谢 @amosbird 又查了一下,我还找到了这个东西:

更好的compile命令

文档中对于compile命令的展示是(compile COMMAND &optional COMINT)

只要按C-u M-x compile RET即可进入 compilation 的 comint-mode 或者调用函数时使用 (compile "COMMAND" t)

注意compile的命令要更改,加上运行程序的部分

附上最后的代码,我将其绑定到了super-r

(defun roife/compile()
  "Compile codes"
  (interactive)
  (if (file-exists-p "Makefile")
		(compile "make -k")
    (when (string= (file-name-extension (file-name-nondirectory buffer-file-name)) "cpp")
		(compile
		 (let ((file (file-name-nondirectory buffer-file-name)))
		 (format "clang++ %s -Wall -g -o %s && time ./%s"
				  file
				  (file-name-sans-extension file)
				  (file-name-sans-extension file))) t)
		(switch-to-buffer-other-window "*compilation*")
		(end-of-buffer)
		)))

这段代码在我这里报错了Symbol’s function definition is void: eshell-exec-visual Mark set

You should enable eshell first.

(require 'eshell)

加上了,仍然是这个错误