Como lidar com nomes de arquivos contendo aspas simples dentro de uma função de conclusão zsh?

Como lidar com nomes de arquivos contendo aspas simples dentro de uma função de conclusão zsh?

Estou usando o zshshell 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/completionpara 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). _vcestá 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 ~/.cheatnã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 Tabapós meu vccomando, 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 cheatsheetsdeve 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; _filespode 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

informação relacionada