2

I heard that Vim has an ex mode which is line-oriented.
That means users can use the editor by echoing commands to it, e.g.,

bash$ echo "123" > 123.txt  # 123.txt: 123
bash$ echo "s/2/0
> w" | vim -e 123.txt       # replace "2" with "0", then save the edit
bash$ cat 123.txt           # 123.txt: 103
103

Is it possible for Emacs to do this?
If yes, how?

Or, more generally,
has Emacs implemented the line-oriented feature?
If not, why?

shynur
  • 4,065
  • 1
  • 3
  • 23

2 Answers2

3

According to @NickD’s answer,
to make Emacs line-oriented just like Vim’s ex mode, use:

$ emacs -batch \
>       -eval "(while t \
>                (write-char ?\\n) \
>                (call-interactively #'eval-expression))"

This will provide you with a read and execute loop.

shynur
  • 4,065
  • 1
  • 3
  • 23
2

emacs -batch can do that:

echo '(progn (find-file "123.txt") (replace-string "2" "0") (save-buffer))' | emacs -batch --eval "(call-interactively #'eval-expression)"

This starts emacs in batch mode where it evaluates its command-line arguments and exits. In this case, the only command line argument is --eval which is asked to call eval-expression interactively. Normally eval-expression reads from the minibuffer, but in batch mode there is no minibuffer and eval-expression reads from stdin. The pipe has arranged for Emacs's stdin to be connected with the stdout of echo, so eval-expression ends up being called with the echo output as its argument. It proceeds to open the file, replace the string and save the buffer. Emacs then exits.

Equivalently but a bit closer in spirit to your example:

echo '(progn (replace-string "2" "0") (save-buffer))' | emacs -batch --file 123.txt --eval "(call-interactively #'eval-expression)"

Here Emacs gets two arguments, a --file argument that visits the file and an --eval argument as before.

NickD
  • 27,023
  • 3
  • 23
  • 42
  • 2
    Strictly speaking, `emacs -batch -eval "(call-interactively #'eval-expression)"` is still a little different from *Vim*'s `ex mode` which is a read and execute **loop**. I also posted an answer based on yours :-) – shynur Feb 26 '23 at 07:49
  • 1
    You are right, although I suspect nobody will want to do it this way: `vim`'s command "language" is simple enough to be able to type it in, but even in that case, a purpose-made filter can be much more convenient (e.g. `sed` or `awk`). OTOH, Emacs is heavily weighted towards interactive use, so the command language is elisp: very flexible and powerful for writing extensions and scripting it but a bit too verbose to use interactively on the command line, IMO. – NickD Feb 26 '23 at 21:07