How can I escape the % and # characters in a Vim command?
Asked Answered
G

4

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.

Gulosity answered 14/4, 2011 at 20:22 Comment(0)
P
13

You just prefix them with a backslash

:!echo %

outputs the current buffer's filename

:!echo \%

prints a solitary '%' character

Percept answered 14/4, 2011 at 22:18 Comment(6)
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?Gulosity
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 itPercept
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.Kotick
@Kotick 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 '#' insteadPercept
Ah! Thanks @sehe, !echo '#' did not work as expected. But ! echo '\#' did.Kotick
Often when things do not work as expected, it is the expectation that was wrong.Percept
W
6

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

:Ack \\\#foo
Wilkison answered 7/4, 2014 at 22:36 Comment(0)
A
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.

Addia answered 15/4, 2011 at 19:48 Comment(5)
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)Percept
@sehe. Yes, of course. It is shell's execve, not vim's. Search back, you will see execve that calls shell.Addia
@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?Addia
You are missing the point. I have shown syntax that (eventually) runs execve with the correct parameters here. 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.Percept
@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.Addia
P
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...

Percept answered 15/4, 2011 at 7:4 Comment(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)Percept

© 2022 - 2024 — McMap. All rights reserved.