
Estoy usando el zsh
shell 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/completion
a 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 vc
es simplemente editar un archivo dentro de una carpeta en particular ( ~/.cheat
). _vc
está 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 ~/.cheat
no 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 Tab
después de mi vc
comando, 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 cheatsheets
debe 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; _files
puede 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