2

Suppose that I have a *shell* buffer (i.e. a subordinate shell, started with M-x shell). Suppose also that at this shell's prompt, I can execute some command foo that sends some output to stdout.

I know that, in general, I can always select the region in the *shell* buffer corresponding to foo's output and add it to the kill-ring with C-w.

I'd like, however, to pipe foo's output directly to the kill-ring, so that it never appears in the *shell* buffer (and thus obviating the need to select the appropriate region and run C-w).

Is there a way to do this?

For example, is there some Unix command bar such that running

% foo | bar

...in the *shell* buffer will cause the output of foo to be added to Emacs's kill-ring?


In case there's a difference between them, I'd like to know the answer to this question for 3 different cases (in order of importance):

  1. Emacs is running as an X11 application (Debian Linux + Xfce4 + Xfwm4);
  2. Emacs is running in "text-only mode" (e.g. started with emacs -nw);
  3. Emacs is running as a Cocoa application in OS X (i.e. it is /Applications/Emacs.app, installed from here);

PS: I just learned that eshell (not to be confused with M-x shell/comint) implements the /dev/kill pseudo-file, which solve's this post's problem (i.e. foo > /dev/kill adds foo's output to the kill-ring). Unfortunately, IME, using eshell has always been an absolute nightmare, since so little of what I know from bash works with eshell; I don't want to learn a whole new---and totally weird--shell from the bottom up just to solve this post's problem. Therefore, at the moment at least, eshell-based solutions are out of the question. This may change, however, depending on the answers I get to another question I just posted.

kjo
  • 3,145
  • 14
  • 42

1 Answers1

5

Emacs Command

Instead of select-and-copy manually, you can also write a command and let it do the work for you:

;; Adapted from `comint-delete-output'
(defun comint-copy-output ()
  "Copy all output from interpreter since last input."
  (interactive)
  (let ((proc (get-buffer-process (current-buffer))))
    (save-excursion
      (let ((pmark (progn (goto-char (process-mark proc))
              (forward-line 0)
              (point-marker))))
    (kill-new (buffer-substring comint-last-input-end pmark))))))

foo | bar

Your idea can also be achieved by a simple shell script. It assumes Emacs server is running, if it is not, you can start it via the command server-start or run Emacs as a daemon.

#!/bin/bash

cat /dev/stdin > /tmp/clip

emacsclient --eval '(kill-new (my-file-contents "/tmp/clip"))'

Becuase it's not easy to embedding Lisp code in shell, my-file-contents is defined in Emacs.

(defun my-file-contents (file)
  (with-temp-buffer
    (insert-file-contents file)
    (buffer-string)))

Now in shell, % foo | bar should do what you want (assuming bar is the shell script and it is in your PATH).

foo > /tmp/kill

In Eshell, you can save output to the kill ring by redirecting output to /dev/kill. You can do the similar in any shell:

  1. redirect output to /tmp/kill
  2. when the contents of /tmp/kill changes, make Emacs copies the new contents to the kill ring

(notes that your Emacs should be compiled with support of watching filesystem, I guess it means (require 'filenotify) should return non-nil)

(defun save-to-kill-ring-if-changes (event)
  ;; (message "Event %S" event)
  (when (eq (cadr event) 'changed)
    (kill-new (my-file-contents "/tmp/kill"))))

;; Create /tmp/kill firstly
(write-region "" nil "/tmp/kill")
(file-notify-add-watch "/tmp/kill" '(change) 'save-to-kill-ring-if-changes)

then in any shell, % foo > /tmp/kill should copy the output of foo to the kill ring.

xuchunyang
  • 14,302
  • 1
  • 18
  • 39