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.

1
  • I came here looking for the /dev/kill eshell thing. Thanks for your edit at the bottom! Commented Nov 13, 2018 at 18:23

1 Answer 1

6

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.

1
  • FYI the file path is /dev/kill , not /tmp/kill
    – RyanWilcox
    Commented Sep 10 at 17:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.