Vim: Pipe selected text to shell cmd and receive output on vim info/command line
Asked Answered
A

7

75

I want to pipe the selected text to a shell command and receive the one-line output from this shell command on the vim info/command line?

What I'm really trying to do: Pipe the selected text to a pastebin-type shell command and I want to receive the output of the shell cmd (which is the http link to the pastebin). Is this possible?

Abdias answered 4/4, 2010 at 19:4 Comment(1)
Related questions for providing selected text as STDIN to shell commands: Replacing the selected original text with the output and Piping to and from the shell, working with entire buffersBerberidaceous
P
19

I would do it like this:

Place this function in your vimrc:

function Test() range
  echo system('echo '.shellescape(join(getline(a:firstline, a:lastline), "\n")).'| pbcopy')
endfunction

This will allow you to call this function by doing:

:'<,'>call Test()

Then you can also map that like this (just under the function declaration in your vimrc):

com -range=% -nargs=0 Test :<line1>,<line2>call Test()

So you can call the function doing this:

:'<,'>Test

Note: :<','> are range selectors, in order to produce them just select the pertinent lines in visual mode and then go to command mode (pressing the colon key)

Preoccupied answered 6/4, 2010 at 14:40 Comment(7)
Great answer and it works. The only thing is it doesn't interpret new lines in vim on the pastebin. So if I have multiple selected lines in vim, it shows up as one big line on paste bin. Is there a way to fix that?Abdias
And for me it produces this: pastebin.com/8XuqTr1K («\» at the end of each line). I'm using pastebin-0.6.1 from Gentoo repository and zsh.Skepful
I'm using sprunge.us and a script I made myself: <code> while read data; do paste="$paste $data" done curl -sF "sprunge=$paste" sprunge.us | xclip && echo xclip -o?sh </code>Abdias
Maybe you should use "$paste\n$data"?Skepful
I fixed my problem with the newline: I changed the '\n' to '\r' in the Test() function above. Now it shows actual new lines on the pastebin.Abdias
Ok, to add another question to this: Do you know how I can get the http link that is shown on the vim system command line by this script to be copied to the system buffer? (ie: "*y)Abdias
I changed this a bit since I was getting trailing '\' characters on each each line to: echo system('pbcopy', join(getline(a:firstline, a:lastline), "\n"))Ingridingrim
H
144

For multi line version you can do this after selecting the text:

:'<,'>:w !command<CR>

See the official Vim docs at :help :w_c.


You can map it to simple Visual mode shortcut like this:

xnoremap <leader>c <esc>:'<,'>:w !command<CR>

Hit <leader key>+c in visual mode to send the selected text to a stdin of the command. stdout of the command will be printed below vim's statusbar.

Real world example with CoffeeScript:

https://github.com/epeli/vimconfig/commit/4047839c4e1c294ec7e15682f68563a0dbf0ee6d

Humberto answered 21/3, 2011 at 2:56 Comment(5)
FYI: omitting :w will replace the selection with the command's output.Conoid
I do not understand the syntax and the mode of action of this :w. Is it the normal "write" command? What is the second : for? Can you explain it? Thanks!Carollcarolle
A variant of the mapping I'm using: xnoremap <expr> <Leader>c "\<Esc>:'<,'>:w !" . getbufvar('%', 'run_command', &filetype) . "\<CR>" .. This uses the buffer's fileype as the command, which works well with many languages (ruby, python, etc.). You can still override this by setting b:run_command which is useful for code embedded in Markdown files and such.Meditate
@Carollcarolle I was wondering the same thing! Found under :help :w, specifically :help :w_c.Afterbrain
This will work with line based selections but if only specific part of the line is selected this will not workTemperature
N
47

Simply highlight the lines using visual line select shift-v, the hit :! and type the command you wish to send the commands to. The resulting output will then replace your selected text.

When you type your command it will appear at the bottom as:

:'<,'>!somecmd

the '<,'> is indicating that the range you have visually selected will be passed to the command specified after the !

Nitza answered 8/4, 2010 at 14:34 Comment(4)
This just replaces the selected text with the output from the shell cmd.Abdias
Sorry, misread the question. I thought that was what you wanted to do.Nitza
This version has it's merrits. After doing this version, you could then copy/yank the commands output. (select using shift-v and hit y, or yy if it's one line, etc) undo to get the original back. (hit u) then paste (hit p) the copied command output. giving you both the original data and the pastebin url in your current buffer, without any extra vim config, and without having to use the mouse to get the commands output back.Blatant
Making somecmd sh will execute the whole line as the actual shell command. Eg. :'<,'>!shCyclotron
P
19

I would do it like this:

Place this function in your vimrc:

function Test() range
  echo system('echo '.shellescape(join(getline(a:firstline, a:lastline), "\n")).'| pbcopy')
endfunction

This will allow you to call this function by doing:

:'<,'>call Test()

Then you can also map that like this (just under the function declaration in your vimrc):

com -range=% -nargs=0 Test :<line1>,<line2>call Test()

So you can call the function doing this:

:'<,'>Test

Note: :<','> are range selectors, in order to produce them just select the pertinent lines in visual mode and then go to command mode (pressing the colon key)

Preoccupied answered 6/4, 2010 at 14:40 Comment(7)
Great answer and it works. The only thing is it doesn't interpret new lines in vim on the pastebin. So if I have multiple selected lines in vim, it shows up as one big line on paste bin. Is there a way to fix that?Abdias
And for me it produces this: pastebin.com/8XuqTr1K («\» at the end of each line). I'm using pastebin-0.6.1 from Gentoo repository and zsh.Skepful
I'm using sprunge.us and a script I made myself: <code> while read data; do paste="$paste $data" done curl -sF "sprunge=$paste" sprunge.us | xclip && echo xclip -o?sh </code>Abdias
Maybe you should use "$paste\n$data"?Skepful
I fixed my problem with the newline: I changed the '\n' to '\r' in the Test() function above. Now it shows actual new lines on the pastebin.Abdias
Ok, to add another question to this: Do you know how I can get the http link that is shown on the vim system command line by this script to be copied to the system buffer? (ie: "*y)Abdias
I changed this a bit since I was getting trailing '\' characters on each each line to: echo system('pbcopy', join(getline(a:firstline, a:lastline), "\n"))Ingridingrim
S
7

Maybe you should use something like

:echo system('echo '.shellescape(@").' | YourCommand')

Starting from some vim-7.4 version it is better to use

:echo system('YourCommand', getreg('"', 1, 1))

. This is basically the only way to keep NUL bytes untouched should they be present in the file. Passing @" in one or the other way will transform NUL bytes into NL (newline).

Skepful answered 4/4, 2010 at 19:45 Comment(3)
This seems good. One minor thing, is after selecting text you have to 'yank' with 'y', before running that command. Just selecting text in visual mode and running that command gives E481: No range allowedHinz
This basic idea can be simplified to :call system('YourCommand', @"). A mapping will do the necessary yank for you. All together, it looks like: map <silent> <Leader>y "9y:call system('YourCommand', @")<CR> The main point is that system() takes a 2nd argument, which is written to the program on stdin, so it avoids all escaping issues.Benedictbenedicta
@TimSmith You are right, but @" is not the best idea. See updated answer.Skepful
M
2

@matias 's solution is not work well for me, because it seems shellescape will append \ to each line.

So I use sed to accomplish this, and it works just fine!

"dump selected lines
function! DumpLines() range
  echo system('sed -n '.a:firstline.','.a:lastline.'p '.expand('%'))
endfunction

com! -range=% -nargs=0 Dump :<line1>,<line2>call DumpLines()
Mainspring answered 10/8, 2014 at 9:49 Comment(0)
S
2

An imperative way to do it is to:

  1. yank your selection
  2. drop into command mode with :
  3. ! + paste the register in the command-line like <Ctrl> r "

So: y : ! <Ctrl> r "

Simpson answered 16/1, 2021 at 18:42 Comment(0)
S
1

Another answer:

function Pastebin() range
    let savedreg=@"
    silent execute a:firstline.",".a:lastline."yank"
    python import vim, subprocess
    python p=subprocess.Popen(["pastebin"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    python p.stdin.write(vim.eval('@"'))
    let @"=savedreg
    python p.stdin.close()
    python retstatus=p.poll()
    python print p.stdout.read()
endfunction

Requires python support. Use it just like matias' function.

Skepful answered 10/4, 2010 at 12:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.