How to make Emacs read buffer from stdin on start?

14,947

Solution 1

Correct, it is impossible to read a buffer from stdin.

The only mention of stdin in the Emacs info pages is this, which says:

In batch mode, Emacs does not display the text being edited, and the standard terminal interrupt characters such as C-z and C-c continue to have their normal effect. The functions prin1, princ and print output to stdout instead of the echo area, while message and error messages output to stderr. Functions that would normally read from the minibuffer take their input from stdin instead.

And the read function can read from stdin, but only in batch mode.

So, you can't even work around this by writing custom elisp.

Solution 2

You could use process substitution:

$ emacs --insert <(echo 123)

Solution 3

You can redirect to a file, then open the file. e.g.

echo 123 > temp; emacs temp

jweede notes that if you want the temp file to automatically be removed, you can:

echo 123 > temp; emacs temp; rm temp

The Emacsy way to do this is to run the shell command in Emacs.

M-! echo 123 RET

That gives you a buffer named *Shell Command Output* with the results of the command.

Solution 4

It is possible, see https://stackoverflow.com/questions/2879746/idomatic-batch-processing-of-text-in-emacs

Here is echo in an emacs script (copied from the above link):

#!/usr/bin/emacs --script
(condition-case nil
    (let (line)
      (while (setq line (read-from-minibuffer ""))
        (princ line)
        (princ "\n")))
  (error nil))

or to read it into a buffer and then print it out all in one go

#!/usr/bin/emacs --script
(with-temp-buffer
  (progn
    (condition-case nil
    (let (line)
      (while (setq line (read-from-minibuffer ""))
        (insert line)
        (insert "\n")))
      (error nil))
    (princ (buffer-string))
    ))

Solution 5

Another possibility not mentioned in any of the previous answers is to use /dev/stdin if your chosen Unix variant has it.

Simply trying to open /dev/stdin directly doesn't work, because Emacs does a few checks and then reports Symbolic link that points to nonexistent file. (And if Emacs would have allowed you to load the file, then trying to save it again as /dev/stdin would rarely do what the user expected.)

However combining /dev/stdin with the --insert argument does work:

echo 123 | emacs --insert /dev/stdin

It should be noted that this version only works when using X. If you need a solution which works in a terminal I suggest you look at another answer.

Share:
14,947

Related videos on Youtube

sastanin
Author by

sastanin

Updated on September 17, 2022

Comments

  • sastanin
    sastanin over 1 year

    With Vim I can easily do

    $ echo 123 | vim -
    

    Is it possible to do with Emacs?

    $ echo 123 | emacs23
    ... Emacs starts with a Welcome message
    
    $ echo 123 | emacs23 -
    ... Emacs starts with an empty *scratch* buffer and “Unknown option”
    
    $ echo 123 | emacs23 --insert -
    ... “No such file or directory”, empty *scratch* buffer
    

    Is it really impossible to read a buffer from a unix pipe?

    Edit: As a solution, I wrote a shell wrapper named emacspipe:

    #!/bin/sh
    TMP=$(mktemp) && cat > $TMP && emacs23 $TMP ; rm $TMP
    
  • sastanin
    sastanin over 14 years
    Yes, I know there is emacsy way, but I hoped it may be used unixy way. Creating a temporary file is not a very nice option (I have to remember to delete it later).
  • jweede
    jweede over 14 years
    tacking on ';rm temp' should delete the file once emacs closes
  • Richard Hoskins
    Richard Hoskins over 14 years
    In general, there is a high impedance between Emacs and Unix. Or at least between Emacs and the traditional Unix work flow.
  • Richard Hoskins
    Richard Hoskins over 14 years
    @jweede If you want to add M-! part of my answer to yours, then I could delete my answer. There is a large overlap in our answers, but I think meta-bang is important to future readers.
  • sastanin
    sastanin over 14 years
    temp may already exist in the current directory, it's not safe; as a solution, I wrote a wrapper: TMP=$(mktemp) && cat > $TMP && emacs23 $TMP ; rm $TMP. Thanks everybody!
  • dbmikus
    dbmikus over 9 years
    This is definitely the answer that gets closest to the Vim functionality. Pretty much just moving the piped part into a subprocess substitution.
  • Andrew Wood
    Andrew Wood over 9 years
    @dbmikus I can't decide which I prefer between mine and Tomasz Obrębski's..
  • dbmikus
    dbmikus over 9 years
    Tomasz's results in me getting the following error for some reason: emacs: standard input is not a tty
  • Andrew Wood
    Andrew Wood over 9 years
    Oh indeed! I assumed he'd tested before posting.
  • user787832
    user787832 over 9 years
    I mean no disrespect to anyone, but this is abhorrent. This is a very basic editor feature and GNU EMACS has been around for decades. It should be built in.
  • Andrew Wood
    Andrew Wood over 9 years
    Hmm.. I just tried Tomasz Obrębski's again, and it works for me this time :/
  • dbmikus
    dbmikus about 9 years
    Maybe it depends on the month.
  • pyrocrasty
    pyrocrasty almost 9 years
    echo 123 | exec emacs -nw --insert <(cat) </dev/tty should work.
  • RoyM
    RoyM over 8 years
    That works; why the exec? This also works: emacs -nw --insert <(echo 123) </dev/tty
  • Charles Roberto Canato
    Charles Roberto Canato over 8 years
    Go figure: works beautifully on Emacs (w32) on Cygwin, where lots of other settings of mine do not
  • Alejandro
    Alejandro about 8 years
    This works well for me, but _emacsfun should be emacsclient -c -t $@, or at the very least drop the -n option. man pages with emacsclient -t --eval "(man \"$1\")" --eval "(delete-window)" (and now you can helm-swoop your way to Man Glory!)
  • Bao Haojun
    Bao Haojun about 7 years
    Thank you very much! Now I have a “wrap” function written with Emacs: cat file-with-long-lines | emacs -nw --batch -Q --insert <(cat) --eval '(progn (fill-paragraph)(message (buffer-string)))' 2>&1
  • rien333
    rien333 over 6 years
    An even more emacs way to do this is to C-u M-! (prefix argument + shell command), which inserts the output at the current point. (the shell to command to execute would be for example cat the/file/you/want)
  • Admin
    Admin about 6 years
    what about (t)csh shell which is the default in the bsd family who lack such a feature?
  • Admin
    Admin about 6 years
    i got emacs: standard input is not a tty message testing it on linux and bsd
  • kasperd
    kasperd about 6 years
    @Chinggis6 Looks like my suggestion only works when using X11. If I first type unset DISPLAY, I get the same error message as you.
  • kasperd
    kasperd about 6 years
    @Chinggis6 I updated my answer to point out that it needs X to work and pointed to an answer which works without X.
  • Alien Life Form
    Alien Life Form almost 6 years
    Too bad it does no work with emacsclient
  • dylnmc
    dylnmc over 5 years
    except when you're in a shell and want to do something like curl foo.bar | vim - .. I'm sorry. I meant curl foo.bar | emacs except you can't do that
  • Admin
    Admin over 5 years
    This is a very cool trick! I used it to make an executable Bash script that's also an Emacs lisp file that is effectively a single-file Emacs app installer and runner. github.com/jgkamat/matrix-client-el/blob/master/misc/…
  • user2751003
    user2751003 about 5 years
    This is the only answer that I could get to work with emacsclient. I made a shell script out of the basic idea so I can call it from i3 config. +1
  • Flux
    Flux about 3 years
    Why not do (let (line) (while (setq line (ignore-errors (read-from-minibuffer ""))) (insert line) (insert "\n"))) instead of using condition-case?