我正在使用zsh
shell 并在我的 中包含以下代码.zshrc
:
fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc
autoload -Uz compinit
compinit
第一行添加~/.zsh/completion
存储在环境变量中的数组的路径fpath
。
第二_vc
行为名为 的自定义 shell 函数加载名为 的完成函数vc
。的目的vc
只是编辑特定文件夹 ( ~/.cheat
) 内的文件。_vc
在文件中定义~/.zsh/completion/_vc
。
最后两行启用完成系统。
这是我的完成功能的代码_vc
:
#compdef vc
declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0
我是从这个复制的地址,并根据我的需要进行了调整。
只要目录中~/.cheat
没有名称包含单引号的文件,补全就会起作用。但如果有一个,例如foo'bar
,则完成将失败并显示以下错误消息:
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
我找到了一个解决方案,将行中的双引号替换cheatsheets="$(ls ~/.cheat)"
为单引号cheatsheets='$(ls ~/.cheat)'
。
现在,当我在命令Tab
后点击时vc
, zsh 会建议 内的文件~/.cheat
,包括foo\'bar
(zsh 似乎已自动转义单引号)。
但是,我不明白它是如何或为什么起作用的。使用单引号时,变量cheatsheets
应包含文字字符串。所以$(...)
命令替换不应该被扩展。例如,如果我执行以下命令:
myvar='$(ls)'
echo $myvar → outputs `$(ls)` literally
那么为什么要'$(ls ~/.cheat)'
在里面进行扩展呢_arguments "1:cheatsheets:(${cheatsheets})" && return 0
?为什么它会自动转义里面的单引号foo'bar
?
答案1
如果我们坚持以错误的方式做事™
#compdef vc
declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0
哎呀!如果文件名包含换行符,这当然会中断,因为(f)
它们会被分割。解析ls
无论如何都是一个坏主意; ZSH 可以将 glob 文件直接放入数组中:
% mkdir ~/.lojban
% touch ~/.lojban/{go\'i,na\ go\'i}
% words=(~/.lojban/*)
% print -l ${words:t}
go'i
na go'i
% words=(~/.lojban/*(On))
% print -l ${words:t}
na go'i
go'i
%
但我们可能不需要本地数组;_files
可以在 glob 上完成:
#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0
这将返回完全限定的文件路径;如果搜索目录中只需要裸文件名,我们可以使用:
#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0