5

Let's say i create a filename with this:

xb@dnxb:/tmp/test$ touch '"i'"'"'m noob.mp4"'
xb@dnxb:/tmp/test$ ls -1
"i'm noob.mp4"
xb@dnxb:/tmp/test$ 

Then vim . to go inside Netrw directory listing.

" ============================================================================
" Netrw Directory Listing                                        (netrw v156)
"   /tmp/test
"   Sorted by      name
"   Sort sequence: [\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$
"   Quick Help: <F1>:help  -:go up dir  D:delete  R:rename  s:sort-by  x:special
" ==============================================================================
../
./
"i'm noob.mp4"

Then press Enter to view the file. Type:

:!ls -l %

It will shows error:

xb@dnxb:/tmp/test$ vim .

ls: cannot access '/tmp/test/i'\''m noob.mp4': No such file or directory

shell returned 2

Press ENTER or type command to continue

I also tried:

[1] :!ls -l '%':

Press ENTER or type command to continue
/bin/bash: -c: line 0: unexpected EOF while looking for matching `"'
/bin/bash: -c: line 1: syntax error: unexpected end of file

shell returned 1

Press ENTER or type command to continue

[2] :!ls -l "%":

Press ENTER or type command to continue
/bin/bash: -c: line 0: unexpected EOF while looking for matching `''
/bin/bash: -c: line 1: syntax error: unexpected end of file

shell returned 1

Press ENTER or type command to continue

[3] :!ls -l expand("%"):

/bin/bash: -c: line 0: syntax error near unexpected token `('
/bin/bash: -c: line 0: `ls -l expand(""i'm noob.mp4"")'

shell returned 1

Press ENTER or type command to continue

[4] !ls -l shellescape("%"):

/bin/bash: -c: line 0: syntax error near unexpected token `('
/bin/bash: -c: line 0: `ls -l shellescape("/tmp/test/"i'm noob.mp4"")'

shell returned 1

Press ENTER or type command to continue

[5] !ls -l shellescape(expand("%")):

/bin/bash: -c: line 0: syntax error near unexpected token `('
/bin/bash: -c: line 0: `ls -l shellescape(expand("/tmp/test/"i'm noob.mp4""))'

shell returned 1

Press ENTER or type command to continue

My ultimate goal is perform rsync by Ctrl+c, e.g:

nnoremap <C-c> :!eval `ssh-agent -s`; ssh-add; rsync -azvb --no-t % xiaobai@127.0.0.1:/home/xiaobai/storage/

My platform is Kali Linux's vim.gtk3, bash. Fedora's vim and gvim also have the same problem.

What's the correct syntax to escape filename containing single and double quotes in vim ?

[UPDATE]

exec '!ls -l' shellescape(expand('%')) can work, but stil i can't figure out how to make rsync above work. I have no idea where should i put quotes for this more complex command rsync.

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.

3

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.

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.