1

Say I have files File1.m, File2.m, and File3.m in the current directory. I want vimgrep to search all but File3.m. After reading the help on using backticks to specify files for vimgrep, I tried:

:vimgrep SearchExpression `echo !(File3).m`

This yields the error

Cannot open file "File1.m File2.m"

So the !(File3).m is being properly expanded by bash, but the space-separated file names that come back are being interpretted in the vimgrep command as a single file name.

Is there a way to have them interpretted as separate files?

I am using [g]vim on cygwin.

1

There's no word splitting done on the string read by Vim. That is, there's nothing special about space characters...they are considered part of the filename. There are a couple ways that come to mind to separate the filenames. This one works...

vimgrep SearchExpression `printf "\%s\n" !(File3).m`

(The % is escaped as Vim will otherwise treat it as special.)

The shell will put every filename on a separate line. Vim understands that each line is a different filename per last paragraph of :h `=.

Tested on Cygwin.

(If any of your filenames have newlines in them, legal in bash, that's a different ball of wax. Vim doesn't deal well with them. I'm guessing that's not an issue for you.)

| improve this answer | |
  • Wow. I had no idea that such a c-like command like printf was available. Did it become available in the last 10 years? That's how long I've been not using bash hard core. Your solution for vimgrep works well. Thanks! – user36800 Jun 30 '19 at 3:29
  • No actually it's been around for a while. Something like Bash 2.0.2 (1998 maybe?). Note, also, that this is for the builtin command. Regardless of what shell you use there has been a generic command of the same name (in /usr/bin or equivalent) since before the Bash builtin. It's part of the POSIX standard. Cheers! – B Layer Jun 30 '19 at 3:43
  • That's not too surprising either....there will always be endless corners of the unix world that I will be constantly discovering. Very useful bits and pieces. Again, thanks for that. – user36800 Jul 1 '19 at 14:20
1

Set 'verbose' to 4, and execute your command again, you should see something like this:

Calling shell to execute: "(echo !(File3).m)>/tmp/vT1eqVi/2"

There is no way to tell whether a space belongs to filename in /tmp/vT1eqVi/2.

Read after :h backtick-expansion and :h `= , you will find this:

If the expression returns a string then names are to be separated with line breaks. When the result is a |List| then each item is used as a name. Line breaks also separate names.

To fix your problem:

vimgrep SearchExpression `ls !(File3).m`

Note that ls is the same as ls -1 here, as it's output is redirected to a file.

| improve this answer | |
  • 1
    ls should never be parsed or its output used for further processing. It can't be relied upon, implementation to implementation, to be consistent for one thing. It also doesn't handle whitespace well. mywiki.wooledge.org/ParsingLs – B Layer Jun 30 '19 at 2:31
  • @BLayer Thanks for the link. – dedowsdi Jun 30 '19 at 3:06
  • 1
    Sure thing! Note that your solution may very well work for every scenario OP ever encounters. But someone, somewhere will get bitten by it. That's why over at Unix & Linux SE parsing ls will get you yelled at. ;) – B Layer Jun 30 '19 at 3:11
  • It works if I replace ls with 'ls'. Somewhere in my bash startup files, I made it so that even when shelling out from vim, I get all my conveniences, e.g., default ls switches, which makes the output unusable by vimgrep. The single quotes around ls ensures that the bare bones version is used. Thanks!! – user36800 Jun 30 '19 at 3:25
  • And if you want to exclude files File2.m and File3.m, you need to escape the vertical bar: :vimgrep SearchExpression `'ls' !(File2\|File3).m`. – user36800 Jul 1 '19 at 14:30

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.