18

I want to run the following shell.

wea-this is the end of the second word | vim j
exit 0

Which I had hoped would pipe the key strokes w (move forward a word) then e (end of the word) then a (append) then -this is the end of the second word (text) to the Vim document named j which already has words in it.

The following error occurred when I ran the shell script.

/root/bin/tt.sh: line 1: wea-this: command not found
Vim: Warning: Input is not from a terminal

I am running Ubuntu 14.04. Two types of answers will be appreciated:

  1. How to use piping to achieve this desired result.
  2. Another method by which I can use "Vim commands" (not ed or sed commands) to edit a text document from a shell script.
  • 1
    The | character pipes the output of one command to another. wea-this is not a command. Piping cannot be used to achieve what you're looking to do either; it'll cause Vim's stdin to be the left hand application, not the keyboard. If you want to get stdin into a buffer in vim, start it with a minus for the filename: echo Hello, world! | vim -. – dash-tom-bang Mar 3 '16 at 19:38
15

To insert the string "hello" on the first line of the file j, you could type in your shell the following command:

vim +"1 | put! ='hello'" j

  • :1 moves the cursor to the first line of the buffer
  • :put! ='hello' pastes the string "hello" above the current line

To insert the string "hello" and then save and quit the file:

vim +"1 | put! ='hello' | x" j

Same thing as before, we've just added the Ex command :x which saves and quit.

More generally you can execute any sequence of Ex commands on a file, from an interactive shell or from a bash script, like this:

vim +'Ex command1 | Ex command2 | ...' file

If you have a lot of Ex commands, you could write them all inside a dedicated file (called for example myExCommands.vim), one command per line, and then from the shell or a script you could source it (with the Ex command :source or the short version:so) like this:

vim +'so myExCommands.vim' file

Edit

I see you've edited your question.
I think the previous answer still applies, because you can execute any normal command from Ex mode with the Ex commands :normal and :execute.

For example, let's say you want to :

  • move your cursor on the first line of the buffer (1G)
  • move your cursor to the first non whitespace character on the line (_)
  • move your cursor one word forward (w)
  • move your cursor to the end of the new word (e)
  • go into insert mode after the character under the cursor (a)
  • insert the string hello
  • save and quit (:x)

To do this on the file j, type from the shell:

vim +"execute 'normal! 1G_weahello' | x" j

For more information, see:

:help +cmd
:help :normal
:help :execute
  • I failed to fully clarify my goal. I want to be able to use vims commands, such as w to move forward a word and such. I will edit my question to correct. – Jason Basanese Dec 29 '15 at 18:33
  • @JasonBasanese I've edited my answer, do the Ex commands :normal and :execute do what you want? – saginaw Dec 29 '15 at 19:00
9

Here is a way you can use normal-mode commands, rather than just ex commands, to edit a stream presented to Vim from stdin. This isn't a very useful example, but it illustrates the techniques involved. It assumes that your shell is bash.

echo "The brown fox jumped." | vim -s <(printf 'wcwred\e:wq! foo\n') -

The - at the end tells Vim to read text (not commands) from stdin. The -s tells Vim to execute keystrokes from the specified file. In this case, the file is the output of a bash process substitution. The <(...) is replaced by the name of a named pipe. The output of the pipe is the output of the command within the parentheses. The wcwred\e moves the cursor by one word and changes that next word to red. The :wq! foo\n saves the result to the file foo even if that file already exists. The end result is a file named foo containing the text The red fox jumped.

For more on some of those topics, see

:help -file
:help -s

See also the Process Substitution section of the bash(1) man page.

  • Added to my ~/.zshrc: vimx () { vi -s <(echo "$1"':wq') "$2" &> /dev/null; }. Now I can vimx 'iThe brow fox jumped.\e' foo; vimx 'wcwred\efjdwiclimbed\e' foo; cat foo. Neat! – Leonard Pauli Apr 29 '18 at 20:02
4

I have found the method of taking the output of a program as redirected from sub-shell to be very easy:

vim <( program )

Example:

 vim <( grep -nH  hello  myFile.cpp )
 vim <( find . -name '*.cpp' -exec grep -nH code_expr {} \+ )
4

I had the exact same desire as you: I wanted to write normal vim commands and have those edit a pipeline stream. I was disappointed by the difficulty of using normal vim in this way (so many flags!), as provided by the default vim options.

Therefore, I wrote a small wrapper on vim to do it: https://github.com/MilesCranmer/vim-stream/. It's called vims.

The heart of the wrapper is the following command:

vim - -nes "${vim_cmds[@]}" -c ':q!' | tail -n +2

Using this, with the command generation I do, it works out to be quite concise, and because vim is a modal editor, I made vims a modal stream editor. There are three main modes which are useful for with pipeline-editing mentality:

[-t|--turn-off-mode] - (Default mode)
                       Every string is a vim command line, e.g., '%g/foo/d' deletes
                       all lines with foo.
[-e|--exe-mode] -      Translated to '%g/$1/exe "norm $2"', see examples below
[-s|--simple-mode] -   Like exe-mode, but runs on 1st line only

Watch this:

$ echo "Hello World" | vims -s "ea Beautiful"
Hello Beautiful World

So, e goes to the end of Hello, then a starts appending.

Something fancier, in exe-mode, to comment out lines containing my_bad_var, and delete the preceding line.

cat my_script.cpp | vims -e 'my_bad_var' 'I//\<esc>kdd'

Which translates to vims '%g/my_bad_var/exe "norm I//\<esc>kdd"' - the I being the command to start insert at the start of the line, and // being the comment sequence. \<esc>kdd pushes the escape key, moves up a line, then deletes the line.

I have 11 total examples on the repo, I encourage you to check them out. Again, this is running on top of vim, it's just a way to make vim more like sed for stream input (but preserving all your beloved vim macros!)

3

This is a fairly old question, but I felt that it was worth contributing that if your given distro/OS has access to moreutils, then you can install it and simply use vipe as a standard addition to vim:

echo foo | vipe # Now you can edit "foo" to be "bar"!

moreutils is super handy for its other tools, too, and I'd highly recommend them if you're a serious command-line user! :) Take a look at the author's introduction (previous link) for more details.

moreutils should be generally available to most *nix-like distros via a package manager and via Homebrew on Mac. For example...

# Arch Linux and MSYS2 (more below) as part of the `community` repo
pacman -Sy moreutils


# Mac OS and Homebrew
brew install moreutils

moreutils is also a part of the MSYS2 package repository, so you can technically use it on Windows too. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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