I am trying to write a command that passes selected text to a shell script.

The command works fine when there is no \n character in the selected text (using Visual mode).

:vmap <Leader>tts 0y:silent exec ":!/home/user/tts.sh ".@0 <Enter>

The problems occurs when I try to select multiple lines. I tried to surround the shell script arguments with escaped quotes

:vmap <Leader>tts 0y:silent exec ":!/home/user/tts.sh \"".@0."\"" <Enter>

But this solution doesn't work either.

How can I do this?

I am using neovim v0.4.4.


Since I posted this the title of the question was changed to explicitly reference arguments. Just in case someone gets the idea that I have reading comprehension issues... ;)

To send multiple lines of Visually selected text to a shell command I'd do it this way:

:vnoremap XXX :w !<shell command> <CR>

If you enter : when text is selected you'll see that the range of lines that are selected is inserted automatically. The same things happens with vmappings so you only need to specify the : then w! (which will send the lines in the current range to a shell program) followed by your shell script path.

As long as your script knows how to process stdin it should be fine.

As a simple example suppose this is my mapping:

vnoremap <Leader>X :w!tac<CR>

(Shell commmand tac reverses whatever text it receives from stdin.)

If I've visually selected these lines:

c c

...and hit <leader>X then I'll see

c c

If you aren't able to leverage stdin and can only use command args that's much stickier. Bash and friends are notoriously tricky when it comes to how white space is handled on the command line (i.e. due to globbing and word splitting). The Vim functions system() and shellescape() will help but unless the types of data you will be sending are relatively constrained you may still have to work at it to get it right. Thus, if you can manage without args and use stdin you really should favor that. (If you didn't have the multi-line input requirement things would be a little easier.)

  • Thanks, using the w method did't work with silent, I solved using the system function – Tony Barletta Oct 5 '20 at 12:44
  • 1
    Glad my answer was helpful. Please upvote it as that's the "approved" way of saying thanks here. I believe there shouldn't be any issue with using silent with w but if you have a solution that works for you I probably won't purse that. (BTW, how are you processing the args in your script...it must be something fairly straightforward.) – B Layer Oct 5 '20 at 14:08
  • The script accept one argument only, as a string. The w method worked too, but in put the output of the script into the buffer ... I don't know why though – Tony Barletta Oct 6 '20 at 16:43
  • Ah, yes, I figured you'd probably be treating the input as a single string arg. Thanks for the acceptance of my answer but are you sure that's what you want to do? I asked for an upvote which is something you can do for multiple answers. You can only accept one answer (it displays that green check mark) and if you're answer is the one that's working for you you should accept that. Either way, I'm happy to keep investigating if you aren't totally satisfied with either answer yet. :w! should not be putting anything in your buffer. Can you show me the exact command you tried? – B Layer Oct 6 '20 at 17:59
  • 1
    Just to be clear, if you're own solution is working to your liking you don't need to do anything else (except fix the vote/accept issue :) . Just wanted to let you know I'm available to provide additional assistance if needed. – B Layer Oct 6 '20 at 18:12

After @B Layer's helpful answer here is the solution I found.

vnoremap <Leader>tts "0y :silent call system("/home/user/tts.sh", @0) <Enter>

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.