Cómo evitar que la finalización de bash reemplace a un personaje al completar la pestaña
bash-completion (2)
Estoy creando un script de finalización de bash para una herramienta que comparte semánticos de carga de archivos con curl.
Con rizo, puedes hacer:
curl -F var = @ archivo
para subir un archivo.
Mi aplicación tiene una semántica similar y deseo poder mostrar posibles archivos después de presionar ''@''. Desafortunadamente, esto está resultando difícil:
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$cur" == @* && "$prev" == ''='' ]]; then
COMPREPLY=( $(compgen -f ${cur:1}) )
return 0
fi
Así que si un comando (hasta ahora) termina con:
abc=@
Los archivos en el directorio actual se mostrarán.
var=@/usr/
/usr/bin /usr/games
El problema es que si realmente presiono la pestaña para completar, ¡la ''@'' desaparece!
var=/usr/bin
Así que parece que bash reemplaza toda la palabra actual con la pestaña COMPREPLY.
La única manera de evitar esto ha sido hacer esto:
COMPREPLY=( $(compgen -f ${cur:1}) )
for (( i=0; i<${#COMPREPLY[@]}; i++ ));
do
COMPREPLY[$i]=''@''${COMPREPLY[$i]}
done
Pero ahora la terminación de la pestaña parece rara por decir lo menos:
@/usr/bin @/usr/games
¿Hay alguna forma de mostrar una finalización de la pestaña del archivo normal (sin el prefijo ''@'') pero conserva la ''@'' al golpear la pestaña?
Entonces, esto me intrigó, así que he estado leyendo la fuente de finalización de bash (disponible en / etc / bash_completion).
Me encontré con esta variable: ${COMP_WORDBREAKS}
, que parece permitir el control sobre qué caracteres se usan para delimitar palabras.
También encontré esta función, _get_cword
y la complementaria _get_pword
, ambas recomendadas para usar en lugar de ${COMP_WORDS[COMP_CWORD]}
y ${COMP_WORDS[COMP_CWORD-1]}
respectivamente.
Entonces, al juntar todo esto, hice algunas pruebas y esto es lo que he encontrado: esto parece funcionar para mí, al menos, espero que también lo sea para ti:
# maintain the old value, so that we only affect ourselves with this
OLD_COMP_WORDBREAKS=${COMP_WORDBREAKS}
COMP_WORDBREAKS="${COMP_WORDBREAKS}@"
cur="$(_get_cword)"
prev="$(_get_pword)"
if [[ "$cur" == ''=@'' ]]; then
COMPREPLY=( $(compgen -f ${cur:2}) )
# restore the old value
COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
return 0
fi
if [[ "$prev" == ''=@'' ]]; then
COMPREPLY=( $(compgen -f ${cur}) )
# restore the old value
COMP_WORDBREAKS=${OLD_COMP_WORDBREAKS}
return 0
fi
Ahora, admitiré que la división if
casos están un poco sucios, definitivamente hay una mejor manera de hacerlo pero necesito más cafeína.
Además, para lo que sea que valga para usted, también descubrí a lo largo del camino el argumento -P <prefix>
para compgen
, lo que evitaría que tenga que pasar por el $COMPREPLY[*]}
después de llamar a compgen
, como
COMPREPLY=( $(compgen -P @ -f ${cur:1}) )
Sin embargo, eso es un poco redundante ante una solución completa.
Prueba esto ....
function test
{
COMPREPLY=()
local cur="${COMP_WORDS[COMP_CWORD]}"
local opts="Whatever sort of tabbing options you want to have!"
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
}
complete -F "test" -o "default" "test"
# complete -F "test" -o "nospace" "sd" ## Use this if you do not want a space after the completed word