¿Crear script de finalización de bash para autocompletar rutas después del signo de igual?

11

Quiero crear un script de finalización de bash que reconozca los argumentos del formulario --argy --some-arg=file.

Después de leer este tutorial y algunos ejemplos /usr/share/bash_completion/completions/, escribí el siguiente script (para ahorrar tiempo al escribir algunas banderas con Chromium):

_chromium() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Some interesting options
    opts="
--disable-web-security
--easy-off-store-extension-install
--incognito
--load-extension=
--pack-extension=
--pack-extension-key=
--user-data-dir=
"
    # Handle --xxxxxx=file
    if [[ ${cur} == "--"*"=" ]] ; then
        # Removed failures (is my logic OK?)
        return 0
    fi

    # Handle other options
    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}
complete -F _chromium chromium

Lo guardé ~/bash_completions/chromiumy creé un enlace simbólico usando sudo ln -s ~/bash_completions/chromium /usr/share/bash_completion/completions/chromium.
Luego lo cargué usando . /usr/share/bash_completions/completions/chromium.

Ahora, estoy experimentando dos problemas:

  • chromium --u<TAB>se expande a chromium --user-data-dir=<SPACE>(no quiero el espacio).
  • Las rutas (directorios y archivos) ya no se completan.

¿Cómo puedo resolver estos problemas?

Rob W
fuente
¿Cambiar a zsh es una opción?
Gilles 'SO- deja de ser malvado'

Respuestas:

11

¡He encontrado la solución a ambos problemas!

  1. Para no agregar un espacio, use la nospaceopción Esto se puede hacer de dos formas:

    • Pásalo a complete:
      complete -o nospace -F _chromium chromium
    • Use el compoptincorporado:
      compopt -o nospace(para habilitar la opción)
      compopt +o nospace(para deshabilitar la opción)

    Lo encontré en la documentación de Bash en gnu.org, 8.7 Builtins de finalización programable .

  2. Terminación de archivos.
    • Peter sugirió usar la -fbandera con compgen. Seguí ese consejo y lo implementé como COMPREPLY=( $(compgen -f "$cur") ). Esto funcionó bien, hasta que intenté completar una ruta que contenía espacios.
      En Stack Overflow, encontré esta respuesta que recomienda la creación manual de la lista de finalización (sin usar compgen). Este enfoque funcionó bien.
    • Use la filenamesopción para decirle a Readline que compspec genera nombres de archivo, por lo que puede:
      • realizar cualquier procesamiento específico de nombre de archivo (como agregar una barra diagonal a los nombres de directorio, citar caracteres especiales o suprimir espacios finales)
      • use diferentes colores para indicar el tipo de archivo (con colored-statshabilitado)

Con la ayuda de la manipulación de cadenas de Bash (para expandir ~y tratar con espacios), construí un script de finalización de bash que cumple con todos los criterios establecidos en la pregunta.

_chromium() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Some interesting options
    opts="
--disable-web-security
--easy-off-store-extension-install
--incognito
--load-extension=
--pack-extension=
--pack-extension-key=
--user-data-dir=
"
    # Handle --xxxxxx=
    if [[ ${prev} == "--"* && ${cur} == "=" ]] ; then
        compopt -o filenames
        COMPREPLY=(*)
        return 0
    fi
    # Handle --xxxxx=path
    if [[ ${prev} == '=' ]] ; then
        # Unescape space
        cur=${cur//\\ / }
        # Expand tilder to $HOME
        [[ ${cur} == "~/"* ]] && cur=${cur/\~/$HOME}
        # Show completion if path exist (and escape spaces)
        compopt -o filenames
        local files=("${cur}"*)
        [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]// /\ }" )
        return 0
    fi

    # Handle other options
    COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
    if [[ ${#COMPREPLY[@]} == 1 && ${COMPREPLY[0]} != "--"*"=" ]] ; then
        # If there's only one option, without =, then allow a space
        compopt +o nospace
    fi
    return 0
}
complete -o nospace -F _chromium chromium
Rob W
fuente
Impresionante respuesta, muchas gracias. Me has ahorrado mucho tiempo. Usé el -finterruptor para completar la ruta del archivo, pero no completa el /carácter si es un directorio ... aunque es algo pequeño.
Jiri Kremser
1

Para completar los nombres de archivo, intente pasar -fa compgen.

Me temo que no podrá deshacerse de los espacios después de las opciones, ya que así es como funciona la finalización: una vez que encuentra una coincidencia única, la completa por completo.

Peterph
fuente
Gracias por la sugerencia de -f. Lo probé, pero no trata correctamente con espacios: la ruta /tmp/foo barda como resultado dos entradas de finalización /tmp/fooy bar. Acerca de lo no deseado --arg=<SPACE>: he encontrado la solución a eso, y lo publiqué en la respuesta a continuación .
Rob W