10

A frequently used feature of Vim for me is filtering a file (or a selection of text) through an external command and replace the selection with the result, e.g.:

:'<,>'!sort

so

c
b
a

will be sorted and will result in

a
b
c

It's also possible to replace the current line with the return value of an external command, e.g.:

:,!ls | wc -l

will insert the number of files in the current directory (in my case e.g.:)

41

But is there a way to pass the string to a command for the shell?

As an example, this might be the content of my visual selection:

line_x
line_y
line_z

I need to execute some shell command and take each of the selected lines as one shell script parameter, e.g.:

my_bash_command line_x -c -e -f
my_bash_command line_y -c -e -f
my_bash_command line_z -c -e -f

What is the best way to do this?

ib.
  • 27,830
  • 11
  • 80
  • 100
oliver
  • 9,235
  • 4
  • 34
  • 39

3 Answers3

12

I suggest you use xargs -L1

Example:

:%!xargs -L1 wc -l

Basically xargs [cmd] will run the following [cmd] with multiple parameters from multiple lines. But with the -L1 argument the [cmd] will be executed command for each line.

If you need to supply a certain order to the arguments for your command you can use xargs's -I option to supply a pattern to be replaced by the argument list.

Example:

:%!xargs -L1 -I {} rake {} --trace

If you feel very adventurous you can just execute the code directly like so:

:%!bash
Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
  • nice and short! thanks! would work in most usecases...but here you cannot add additional flags to the command: e.g. `rake xyz --trace` where `xyz` is the text of one line. ...or can you? – oliver Jul 20 '11 at 15:17
  • 2
    @oliver: can you not run `rake --trace xyz`? You could use the `-I` option to supply a pattern to replace with the argument list. e.g. `xargs -L1 -I {} rake {} --trace` – Peter Rincker Jul 20 '11 at 15:46
  • you are good man! although I regularly use xargs on the command line I never used the `-I` replace-str option! Works like a charm! This tip will be useful too me in other scenarios as well! – oliver Jul 20 '11 at 16:11
  • @oliver: I have updated my answer to show the `-I` option. Glad to share the wonders of `xargs`. – Peter Rincker Jul 20 '11 at 16:25
2
echo map(getline("'<", "'>"), 'system(v:val)[:-2]')

:h map()
:h system()
" [:-2] ~> chomp

You can use line insertion functions instead of :echo if you prefer (:put, setline(), etc).

Luc Hermitte
  • 31,979
  • 7
  • 69
  • 83
  • wow...looks a little involved but works! I tried to download a list of urls: `echo map(getline("'<","'>"), 'system("wget ".v:val)')` and that did the job! thanks! – oliver Jul 20 '11 at 15:11
1

A macro is probably the easiest way. Put you cursor on the start of the first line and record yourself doing it to one. Then you can run the macro over many lines. For instance, this will pass the argument to echo and replace the results into the line with the argument. Type these with your cursor somewhere on the first line of the commands.

qa0y$:r!echo <C-r>"<Enter>kddjq

This line recorded a macro into the register a (qa), moved to the start of the line (0), copied the entire line (y$), goes into command line mode (:), read the result from running a command into the line below yours (r!echo ...), the C-r followed by " pastes your text into the :r command, k moved you back to your original line, dd deleted the line, j moved you to the next line (should contain your next command), and then stopped recording (q).

Now you can run the macro on each line either by pressing @a (after doing it once, @@ repeats the last run macro) or by hitting the number of times you want to run the macro 5@a. Or even better highlight the lines and press :norm! @a

It looks like a lot when typed out, however when you get used to macros they are a real time saver. You basically do what you want to the first line and then have Vim replay your key strokes over and over again. It does take a little practice to see the problem as a set of repeatable tasks, but isn't that what we are good at doing anyway? :)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Neg_EV
  • 1,930
  • 1
  • 15
  • 23
  • By the way I have to give credit for the part where I run the macro over multiple lines to: http://stackoverflow.com/questions/390174/in-vim-how-do-i-apply-a-macro-to-a-set-of-lines I have used macros for years and used to just hold @@ or count the lines and hit N@@. The tip in that answer was a real time saver for me. – Neg_EV Jul 20 '11 at 14:37
  • macros in VIM are sooooo nice! can't live without them! and thanks to you I have another useful scenario of using macros. I especially didn't know about the `:norm!` option! – oliver Jul 20 '11 at 15:56