
Estou usando o zsh
shell e tenho o seguinte código dentro do meu .zshrc
:
fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc
autoload -Uz compinit
compinit
A primeira linha adiciona o caminho ~/.zsh/completion
para o array armazenado dentro da variável de ambiente fpath
.
A segunda linha carrega uma função de conclusão, chamada _vc
, para uma função shell personalizada chamada vc
. O objetivo vc
é simplesmente editar um arquivo dentro de uma pasta específica ( ~/.cheat
). _vc
está definido no arquivo ~/.zsh/completion/_vc
.
As últimas 2 linhas permitem um sistema de conclusão.
Aqui está o código da minha função de conclusão _vc
:
#compdef vc
declare -a cheatsheets
cheatsheets="$(ls ~/.cheat)"
_arguments "1:cheatsheets:(${cheatsheets})" && return 0
eu copiei dissoendereço, e adaptei-o para minhas necessidades.
Contanto que o diretório ~/.cheat
não possua um arquivo cujo nome contenha aspas simples, a conclusão funciona. Mas se houver um, como foo'bar
, a conclusão falhará com esta mensagem de erro:
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
Encontrei uma solução, substituindo as aspas duplas na linha cheatsheets="$(ls ~/.cheat)"
por aspas simples cheatsheets='$(ls ~/.cheat)'
.
Agora, quando eu clico Tab
após meu vc
comando, zsh sugere arquivos dentro de ~/.cheat
, incluindo foo\'bar
(zsh parece ter escapado da aspa simples automaticamente).
No entanto, não entendo como ou por que isso funciona. Com aspas simples, a variável cheatsheets
deve conter uma string literal. Portanto, a $(...)
substituição do comando não deve ser expandida. Por exemplo, se eu executar os seguintes comandos:
myvar='$(ls)'
echo $myvar → outputs `$(ls)` literally
Então, por que é '$(ls ~/.cheat)'
expandido por dentro _arguments "1:cheatsheets:(${cheatsheets})" && return 0
? E por que ele escapa automaticamente da aspa simples dentro de foo'bar
?
Responder1
Se insistirmos em fazer as coisas da maneira errada™
#compdef vc
declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0
Que nojo! É claro que isso será interrompido caso o nome de um arquivo contenha uma nova linha, pois é (f)
dividido nelas.Analisar ls
é de qualquer maneira uma má ideia; ZSH pode agrupar arquivos diretamente em um array:
% 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
%
Mas provavelmente não precisamos de um array local; _files
pode ser concluído em um globo:
#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0
Isso retorna caminhos de arquivos totalmente qualificados; se apenas o nome do arquivo simples for necessário em um diretório de pesquisa, podemos usar:
#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0