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

4

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 ao 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 de shell personalizada chamada vc . O objetivo de vc é simplesmente editar um arquivo dentro de uma pasta específica ( ~/.cheat ). _vc é definido no arquivo ~/.zsh/completion/_vc .

As últimas 2 linhas ativam um sistema de conclusão.

Este é 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

Copiei este endereço e adaptei-o às minhas necessidades .

Contanto que o diretório ~/.cheat não tenha um arquivo cujo nome contenha uma aspa simples, a conclusão funciona. Mas se houver um, como foo'bar , a conclusão falhará com essa mensagem de erro:

(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '
(eval):56: unmatched '

Eu encontrei uma solução, substituindo as aspas duplas na linha cheatsheets="$(ls ~/.cheat)" , com aspas simples cheatsheets='$(ls ~/.cheat)' .

Agora, quando eu bato Tab após o meu comando vc , o zsh sugere arquivos dentro de ~/.cheat , incluindo foo\'bar (zsh parece ter escapado da aspas simples automaticamente).

No entanto, não entendo como ou por que funciona. Com aspas simples, a variável cheatsheets deve conter uma cadeia 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)' foi expandido dentro de _arguments "1:cheatsheets:(${cheatsheets})" && return 0 ? E por que ele automaticamente escapa a aspas simples dentro de foo'bar ?

    
por user547381 22.05.2017 / 15:10

1 resposta

3

Se insistirmos em fazer as coisas The Wrong Way ™

#compdef vc

declare -a cheatsheets
cheatsheets=(${(f)"$(ls ~/.cheat/)"})
_arguments '1:cheatsheets:(${cheatsheets})' && return 0

Eca! Isso, é claro, será interrompido se um nome de arquivo contiver uma nova linha, pois (f) se divide nesses. De qualquer forma, analisar ls é uma má ideia ; O ZSH pode globar arquivos diretamente em uma matriz:

% 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 glob:

#compdef vc
_arguments '1:cheatsheets:_files -g "~/.cheat/*"' && return 0

Isso retorna caminhos de arquivos completos; se apenas o nome de arquivo simples for necessário em um diretório de pesquisa, podemos usar:

#compdef vc
_arguments '1:cheatsheets:_files -W ~/.cheat' && return 0
    
por 22.05.2017 / 16:26