¿Cómo lidiar con nombres de archivos que contienen una comilla simple dentro de una función de finalización de zsh?

¿Cómo lidiar con nombres de archivos que contienen una comilla simple dentro de una función de finalización de zsh?

Estoy usando el zshshell y tengo el siguiente código dentro de mi .zshrc:

fpath=(~/.zsh/completion $fpath)
autoload -Uz _vc

autoload -Uz compinit
compinit

La primera línea agrega la ruta ~/.zsh/completiona la matriz almacenada dentro de la variable de entorno fpath.

La segunda línea carga una función de finalización, llamada _vc, para una función de shell personalizada llamada vc. El propósito de vces simplemente editar un archivo dentro de una carpeta en particular ( ~/.cheat). _vcestá definido en el archivo ~/.zsh/completion/_vc.

Las últimas 2 líneas habilitan un sistema de finalización.

Aquí está el código para mi función de finalización _vc:

#compdef vc

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

lo copie de estoDIRECCIÓNy lo adapté a mis necesidades.

Siempre que el directorio ~/.cheatno tenga un archivo cuyo nombre contenga una comilla simple, la finalización funciona. Pero si hay uno, como por ejemplo foo'bar, la finalización falla con este mensaje de error:

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

Encontré una solución reemplazando las comillas dobles en la línea cheatsheets="$(ls ~/.cheat)"por comillas simples cheatsheets='$(ls ~/.cheat)'.

Ahora, cuando presiono Tabdespués de mi vccomando, zsh sugiere archivos dentro de ~/.cheat, incluido foo\'bar(zsh parece haber escapado de la comilla simple automáticamente).

Sin embargo, no entiendo cómo ni por qué funciona. Entre comillas simples, la variable cheatsheetsdebe contener una cadena literal. Por tanto, la $(...)sustitución de comandos no debería ampliarse. Por ejemplo, si ejecuto los siguientes comandos:

myvar='$(ls)'
echo $myvar    →    outputs `$(ls)` literally

Entonces ¿por qué se '$(ls ~/.cheat)'expande por dentro _arguments "1:cheatsheets:(${cheatsheets})" && return 0? ¿Y por qué se escapa automáticamente la comilla simple dentro foo'bar?

Respuesta1

Si insistimos en hacer las cosas del modo equivocado™

#compdef vc

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

¡Qué asco! Por supuesto, esto se interrumpirá si el nombre de un archivo contiene una nueva línea, como(f) se divide en ellas.Analizandols es de todos modos una mala idea; ZSH puede agrupar archivos directamente en una 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
% 

Pero probablemente no necesitemos una matriz local; _filespuede completar en un globo:

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

Esto devuelve rutas de archivo completas; Si solo se requiere el nombre del archivo simple de un directorio de búsqueda, podemos usar:

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

información relacionada