3

I'm executing an external command that takes as input a range of lines within my file.

:'<,'>!copy_stuff

Running this runs copy_stuff correctly with the currently selected visual range (and it also works fine with any other range). However, since copy_stuff doesn't write anything to stdout, it also deletes that range.

Is there any way to pass a range as input to a command without deleting the lines in that range?

2 Answers 2

7

You can use :write for this:

:'<,'>w !copy_stuff

For more details, see :help :write_c:

Execute {cmd} with [range] lines as standard input (note the space in front of the '!').

2
  • 3
    Hah! Of course it was this simple... Amazing how you forget about this stuff. And here I was defining user commands for it...
    – filbranden
    Commented Jan 30, 2020 at 17:27
  • 3
    @filbranden Oh I definitely know that feeling.
    – Rich
    Commented Jan 30, 2020 at 17:30
2

Rich's answer using :'<,'>w !copy_stuff is the right answer!


One alternative is to use the system() function.

You can pass it a string as input, which you can get from a register that captures the text in your range.

It's convenient to define a user command for this purpose, since then you can easily pass it a range.

For example, with your copy_stuff:

function! CopyStuff(line1, line2)
    let save_reg = getreg('"')
    let save_type = getregtype('"')
    silent execute a:line1 ',' a:line2 'y'
    call system('copy_stuff', @")
    call setreg('"', save_reg, save_type)
endfunction

command! -range=% -bar CopyStuff call CopyStuff(<line1>, <line2>)

The first two lines and last line of the function body are saving and restoring the default register (so that it doesn't affect your put commands.)

The third line yanks the range (passed as arguments) into the default register.

Finally, the system() call passes the contents of the default register as input for the external command.

The user-defined command is using -range=%, which allows it to take a range, and defaults to the whole buffer if one isn't passed (you can omit the =% part if you prefer to default to the current line.)

The <line1> and <line2> placeholders are replaced with the first and last line of the range when the command is executed.

The -bar is there because it's good to have it, always, to be able to put another command on the same line after a |. (Only exception if you want to handle |s yourself in arguments, but that's seldom the case.)

Invoke it with:

:CopyStuff

Or from visual mode:

:'<,'>CopyStuff

See :help :command for more options available for user-defined commands (such as arguments).

Your Answer

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

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