40

I want to select a block of text (for example, V%) and use the text as input to a shell command (for example, wc or pbcopy) - but I don't want to alter the current buffer - I just want to see the output of the command (if any) then continue editing without any changes.

Typing V%!wc translates to :'<,'>!wc and switches the block of text for the output of the wc command.

How do you pipe a chunk of text to an arbitrary shell command without affecting the current buffer?

1
  • If you want to pipe your text selection into the OS clipboard (pbcopy on OS X), you can just use the * yank buffer. In visual mode type: "*y Commented Aug 23, 2013 at 7:56

4 Answers 4

95

Select your block of text, then type these keys :w !sh

The whole thing should look like:

:'<,'>w !sh

That's it. Only took me 8 years to learn that one : )

note: typing : after selecting text produces :'<,'> a range indicating selection start and end.

Update 2016: This is really just one use of the generic:

'<,'>w !cli_command

Which basically lets you "send" arbitrary parts of your file to external commands and see the results in a temporary vi window without altering your buffer. Other useful examples would be:

'<,'>w !wc
'<,'>w !to_file my_file

I honestly find it more useful to alter the current buffer. This variety is simply:

'<,'>!wc
'<,'>!to_file my_file
Sign up to request clarification or add additional context in comments.

3 Comments

Awesome, neat and builtin --- all one could hope for.
Documented at :help :write_c for those curious.
This would echo the output of the command on status bar - is there a way to recall or copy those messages? I tried :messages but it doesn't show them.
4

One possibility would be to use system() in a custom command, something like this:

command! -range -nargs=1 SendToCommand <line1>,<line2>call SendToCommand(<q-args>) 

function! SendToCommand(UserCommand) range
    " Get a list of lines containing the selected range
    let SelectedLines = getline(a:firstline,a:lastline)
    " Convert to a single string suitable for passing to the command
    let ScriptInput = join(SelectedLines, "\n") . "\n"
    " Run the command
    let result = system(a:UserCommand, ScriptInput)
    " Echo the result (could just do "echo system(....)")
    echo result
endfunction

Call this with (e.g.):

:'<,'>SendToCommand wc -w

Note that if you press V%:, the :'<,'> will be entered for you.

:help command
:help command-range
:help command-nargs
:help q-args
:help function
:help system()
:help function-range

2 Comments

My first thought was that it looked really chunky and verbose, but after adding chucking it in my .vimrc and adding a mapping, it's perfect: vmap # :SendToCommand<Space> Now I can just do V%# and bang out the command name. Just what I wanted.
Glad you like it! Your mapping looks like a good idea (I do tend to go for long and verbose command names and rely on tab-completion to save me the effort). Consider putting it in ~/.vim/plugin/sendtocommand.vim or ~/.vim/autoload/sendtocommand.vim (the latter will require some changes) to help keep your vimrc manageable.
1

Update: my answer is nonsense.

@pixelearth's answer is good, but I had a little trouble understanding what he did exactly, so I wrote the following. This sequence of commands let's you execute wc -l on your visual selection. wc -l simply counts the number of lines passed to it.

  1. In Vim go into Visual Mode using v
  2. Select a few lines by going down: jjjj
  3. Type : which Vim will translate to :'<,'>
  4. Type w !wc -l, your complete commandline should now be :'<,'>w !wc -l
  5. Press Enter to get the result of your command (in this example it would be 4)
  6. Press Enter to continue editing

I don't understand what exactly happens at step 3 and 4 but I do know that it works.

5 Comments

This isn't really the same thing at all. What you're doing is manually typing the command !wc -l, what is being done above is that the command you want to execute IS ALREADY IN THE VIM FILE. So if you had wc -l on a line by itself, you could use the method above to pipe THAT command to !sh (the shell).
@pixelearth You're right, I just did not understand the original question.
Revisiting this after several years, I can see that while what you're doing isn't exactly the same thing, it's still really cool. You're basically using the selected text as INPUT for following command. Equally useful.
For those confused about the '<,'> bit: check out :help '< and :help mark-motions inside Vim. Things that start with apostrophe (') are "marks". Vim has these special marks for the beginning and end of the visual selection. A colon-command in Vim can be applied to a given region of the file by specifying that region (with marks, or other methods) just after the colon. So :'<,'>... reads as "From mark < to mark >, run command ..."
If your answer is nonsense (as you state), why don't you delete it?
0

I know it's not the ideal solution, but if all else fails, you could always just press u after running the command to undo the buffer change.

2 Comments

Sure, that's what I do currently - I'm just hoping I'm missing something. I guess if there isn't a built-in way of doing this, a small <code>cmap</code> could do the trick - anyone got a ready-made one?
After glancing around on meta-SO, using backticks around text apparently is supposed to allow code-formatting in comments.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.