vim - How to escape filename containing single and double quotes mix?
Building on Wildcard's answer using filename modifiers, :S
gives you exactly what you want. According to the docs (:h %:S
),
:S Escape special characters for use with a shell command (see
|shellescape()|). Must be the last one. Examples:
:!dir <cfile>:S
:call system('chmod +w -- ' . expand('%:S'))
To use your example:
$ touch '"I'\''m also a n00b.txt"'
$ ls
"I'm also a n00b.txt"
Then vim '"I'\''m also a n00b.txt"'
, and voilà:
:!ls %:S
"I'm also a n00b.txt"
The :S
filename modifier is available in Vim 7.4.
From :help filename-modifiers
:
The file name modifiers can be used after "%", "#", "#n", "<cfile>", "<sfile>",
"<afile>" or "<abuf>". ...
...
:s?pat?sub?
Substitute the first occurrence of "pat" with "sub". This
works like the |:s| command. "pat" is a regular expression.
Any character can be used for '?', but it must not occur in
"pat" or "sub".
After this, the previous modifiers can be used again. For
example ":p", to make a full path after the substitution.
:gs?pat?sub?
Substitute all occurrences of "path" with "sub". Otherwise
this works like ":s".
So rather than just handling double quotes or single quotes, let's just backslash escape everything unusual:
:!ls -l %:gs/[^0-9a-zA-Z_-]/\\&/
Works perfectly with the test filename you provided.
To use an absolute path, which you may want for rsync
, you can add :p
at the end:
:!ls -l %:gs/[^0-9a-zA-Z_-]/\\&/:p
Actually, it also works just fine if you backslash-escape literally every character, and it's shorter to type:
:!ls -l %:gs/./\\&/:p
So, in your rsync
command, instead of %
, use %:gs/./\\&/:p
.