14

I'm using Ack (https://github.com/mileszs/ack.vim) with the --literal flag to search through projects in Vim. I noticed that whenever I search for a string with the % or # characters, the search doesn't match things as I'd expect it to. I did some research and found that it's because Vim will expand these characters in commands (% is the current file and # to something else, not sure what).

This is pretty annoying behavior when performing a search, considering these symbols come up pretty often in code. Is there a way to escape them, preferably automatically, so that the search works as expected? My current mapping is: nnoremap <leader>al :Ack --literal<space>.

Example

Say I have a selector #body in a CSS file someplace and I want to find it. These are the things I've tried (that haven't worked):

:Ack --literal #body
:Ack --literal \#body
:Ack --literal "#body"
:Ack --literal "\#body"

Any ideas why escaping wouldn't work as usual here, or what this is even searching for? I haven't had these examples match anything.

Solution

I've gotten it to work by double-escaping the characters. For example, :Ack --literal "\\#body" will show :ack -H --nocolor --nogroup --column --literal "#body" in the statusline of the result window and bring up the expected results. The quotes seem to be required as well.

John Debs
  • 3,714
  • 2
  • 25
  • 35

4 Answers4

12

You just prefix them with a backslash

:!echo %

outputs the current buffer's filename

:!echo \%

prints a solitary '%' character

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for the answer. Your example works as expected, but I've updated my question to include an example where this type of escaping doesn't work. Any ideas as to why not/what can be done? – John Debs Apr 15 '11 at 04:57
  • 1
    It is likely a bug in ack.vim (where it passes the input without re-escaping it). I'd contact the author / see if a new version fixes it – sehe Apr 15 '11 at 06:55
  • In Vim versin 8.1, I had to escape `#` using double slashes like `\\#` otherwise it did not work. For example `! echo \#` did not print anything but `! echo \\#` printed the hash character. – RajaRaviVarma Sep 20 '19 at 09:32
  • @RajaRaviVarma that's not actually any different. It's because `#` is the shell comment character. `!echo \#` actually does send `echo #` to the shell. You can easily verify that `echo #` doesn't actually print anything in posix shells. So, mystery solved. Adding the second slash sends `echo \#` to the shell. I'd suggest `!echo '#'` instead – sehe Sep 20 '19 at 10:37
  • Ah! Thanks @sehe, `!echo '#'` did not work as expected. But `! echo '\#'` did. – RajaRaviVarma Sep 20 '19 at 10:56
  • Often when things do not work as expected, it is the expectation that was wrong. – sehe Sep 20 '19 at 10:58
6

Apparently you have to escape multiple times as mentioned in an ack.vim issue:

:Ack \\\#foo
brianpeiris
  • 10,735
  • 1
  • 31
  • 44
4

I have one addition to @sehe's answer: when you do !... or system('...') vim does not process ... by itself, but calls shell, like this: {shell} {shellcmdflag} {shellxquote}...{shellxquote}. For ack call this will be something like ["/bin/bash", "-c", "ack -H --nocolor --nogroup --literal #body"], so bash will ignore everything after --literal because # is a comment character. It won't do so for '#body' because no comments are possible inside a quoted string.

ZyX
  • 52,536
  • 7
  • 114
  • 135
  • strace shows execve. This will not invoke any shell at any point. It will directly call a binary with the exact null-delimited arguments (i.e. no expansions of any kind) – sehe Apr 16 '11 at 20:25
  • @sehe. Yes, of course. It is shell's execve, not vim's. Search back, you will see execve that calls shell. – ZyX Apr 17 '11 at 05:44
  • 1
    @sehe. Or try to explain why `:!trap USR1` does not show any errors while `trap` executable does not exist (because `trap` can be implemented only as a shell builtin). Also see 'shell' option and friends: if vim does not call shell, then what are these for? – ZyX Apr 17 '11 at 05:50
  • You are missing the point. I have shown syntax that (_eventually_) runs execve with the correct parameters [here](http://stackoverflow.com/questions/5669194/how-can-i-escape-the-and-characters-in-a-vim-command/5673403#5673403). At that stage it really doesn't matter how many levels of shell had been involved. The ack invocation is shown to be correct at that time. – sehe Apr 17 '11 at 17:20
  • @sehe: It matters: you have explained *what* is the correct syntax, I explained *why* it is correct. If you find somewhere a shell that does not use `'` as a quotation character (like cmd.exe), then this won't be a correct syntax, but my answer will still be applicable and will enable OP to find out what is the issue. Your answer won't. – ZyX Apr 17 '11 at 22:03
3

It is a bug in ack.vim, somehow the ack program isn't even being called when you do :Ack --literal \#body

However, I used

`strace -f -o-e trace=process gvim | tee /tmp/log`

And it seems that doing :Ack --literal '\#body' (mind the extra quotes) does work as expected:

[pid  3833] execve("/usr/bin/ack", ["ack", "-H", "--nocolor", "--nogroup", "--literal", "#body"], [/* 25 vars */] <unfinished ...>

I haven't really tested it...

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    @John: did you have any success using this syntax `'\#body'`? The execve sure suggests that it should work (but I don't have ack) – sehe Apr 17 '11 at 17:22