5

An example:

A visual selection of:

 echo -e "hello
 \bworld"

followed by :w !bash prints:

hello 
world 

How can I get the same result if my starting point is:

hello
\bworld

More details:

  • I would like to pass a visual selection to a command that looks like this:

    some_command -some_flag "selection_from_vim"
    
  • The visual selection from vim should be placed inside quotes in the command line command.

  • Like in the example, I would like to be able to select several lines.
7
  • Thanks @dedowsdi. Not sure if I follow myself now. From vimdoc.sourceforge.net/htmldoc/editing.html I see that w_c is used to "Execute {cmd} with [range] lines as standard input" which sounds indeed like what I want. How would I use it though to be able to execute a command like some_command -some_flag "selection_from_vim"?
    – Flo
    Apr 17, 2020 at 6:11
  • @Fio IIUC, It has nothing to do with vim, you just need to make sure your shell command can read input from stdin.
    – dedowsdi
    Apr 17, 2020 at 6:18
  • Ah, I see. Thanks, will post on stackoverflow instead then! Shall I delete this question from here then @dedowsdi?
    – Flo
    Apr 17, 2020 at 6:23
  • 1
    Feel free to do whatever you want, it's still a bit vim related.
    – dedowsdi
    Apr 17, 2020 at 6:26
  • 1
    The body of the question is: "how do I run a command based on the selection in vim"—perfectly on topic (although a dupe may exist). Leave it here, and don't cross-post (on Stack Overflow or anywhere else... that's discouraged on the network). P.S. Welcome to Vi and Vim!
    – D. Ben Knoble
    Apr 17, 2020 at 15:35

1 Answer 1

3

There's a shell solution for your issue and a Vim solution to it.

Shell solution

The shell solution to your issue is to have the shell read the contents (two lines with hello and \bworld) from standard input, then assemble the echo -e ... command and execute it.

You can do so using the shell's eval command, to reinterpret the assembled string as a shell command. You can use $(cat) to capture the standard input as a string in the command to be evaluated.

:w !eval 'echo -e "'"$(cat)"'"'

Note that this is a concatenation of three strings, first being echo -e ", including the opening double quote and the last being a single double quote. The middle one "$(cat)" is itself inside double quotes in order to preserve spaces and newlines (that's before eval has a run on it.) Note that this solution is fragile, as if your input has any "s in it, it will potentially break the command. But it's the closest one to your original example and to what you specified.

Vim solution

A solution within Vim is to use the :! command instead and assemble the shell command, in this case echo, inside Vim itself.

You can use :execute to run the command from a Vim string, and shellescape() to quote the argument to echo. Note that in this case you'll need to pass a non-zero second argument to shellescape(), so it escapes the newlines with a backslash, which makes it work correctly with :!.

First yank the visual selection into the default register with a y command.

Then execute the external command with:

:execute '! echo -e '.shellescape(@", 1)

(Here @" is the default register.)

This solution is quite robust to characters in the input, since shellescape() is doing the job of escaping special characters and quoting the string. (Note how we don't have any "s or 's that are passed to the shell, those are only Vim strings here.)

There's a small difference in how Vim runs :w !{cmd} and :!{cmd} here, in that with the former it doesn't clear the screen (switch back from the alternate screen) but does so with the latter.

One hacky workaround is to still use :w !{cmd} for the second case, perhaps with a small range of the current line. It will pass the external command that line as standard input, but as long as the command doesn't use the standard input, that shouldn't make a difference.

:execute '.w !echo -e '.shellescape(@", 1)
4
  • Can you write directly into eval? Is it a binary too, or do you need sh-c, or does ! imply that? Much confusion (not saying youre wrong, just wondering)
    – D. Ben Knoble
    Apr 17, 2020 at 13:52
  • 1
    @D.BenKnoble Using :! will run the command on a shell. See :help 'shell'. I guess my answer is non-Windows-friendly in that sense... But then the question looked pretty Unix/Linux specific to me... And eval is not a binary, it's a built-in of the shell.
    – filbranden
    Apr 17, 2020 at 14:16
  • 2
    i knew it was builtin, that’s why i was confused :) i wasnt sure you could write directly into it without a layer of shell evaluation (and sometimes I forget that ! Implies shell and not just exec with a PATH)
    – D. Ben Knoble
    Apr 17, 2020 at 14:38
  • 1
    @D.BenKnoble Direct exec typically requires being able to pass a list (like system() or systemlist() accept, or in other programming languages see Python's subprocess module), since otherwise it's not really possible to pass arguments with spaces or blanks. So yeah if you pass it just a string, and particularly if you can pass it a quoted string, that typically means it's passed to a shell on your behalf... (There are exceptions.)
    – filbranden
    Apr 17, 2020 at 14:44

Your Answer

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

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