I'm using the zsh
shell and have the following code inside my .zshrc
:
fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc
autoload -Uz compinit
compinit
The first line add the path ~/.zsh/completion
to the array stored inside the environment variable fpath
.
The 2nd line loads a completion function, called _vc
, for a custom shell function called vc
. The purpose of vc
is simply to edit a file inside a particular folder (~/.cheat
). _vc
is defined in the file ~/.zsh/completion/_vc
.
The last 2 lines enable a completion system.
Here's the code for my completion function _vc
:
#compdef vc
declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0
I copied it from this address, and adapted it for my needs.
As long as the directory ~/.cheat
doesn't have a file whose name contains a single quote, the completion works. But if there's one, such as foo'bar
, the completion fails with this error message:
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
I found a solution, by replacing the double quotes in the line cheatsheets="$(ls ~/.cheat)"
, with single quotes cheatsheets='$(ls ~/.cheat)'
.
Now, when I hit Tab
after my vc
command, zsh suggests files inside ~/.cheat
, including foo\'bar
(zsh seems to have escaped the single quote automatically).
However, I don't understand how or why it works. With single quotes, the variable cheatsheets
sould contain a literal string. So the $(...)
command substitution shouldn't be expanded. For example, if I execute the following commands:
myvar='$(ls)'
echo $myvar → outputs `$(ls)` literally
So why is '$(ls ~/.cheat)'
expanded inside _arguments "1:cheatsheets:(${cheatsheets})" && return 0
? And why does it automatically escape the single quote inside foo'bar
?