bash completado para patrones de nombre de archivo o directorios


12

Estoy tratando de configurar un script de finalización de bash y tengo algunos problemas.

Me gustaría configurarlo para que las terminaciones enumeradas sean archivos que coincidan con una extensión particular o directorios (que pueden contener o no archivos de esa extensión).

El problema que tengo es que la única forma en que puedo obtener las terminaciones para contener archivos y directorios es usando algo como -o plusdirs -f -X '!*.txt', pero cuando dejo que bash complete uno de los directorios, solo agrega un espacio al final, en lugar de un barra oblicua.

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}
  local prev=${COMP_WORDS[COMP_CWORD-1]}

  #COMPREPLY=( $( compgen -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -f -G '*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o filenames -f -X '!*.txt' -- $cur ) )
  #COMPREPLY=( $( compgen -o dirnames  -f -X '!*.txt' -- $cur ) )
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
  return 0
}

complete -F _xyz xyz

También probé todas las líneas comentadas, pero ni siquiera expanden los directorios.

Para las pruebas, he estado ejecutando esto en un directorio con un archivo .txt y un directorio "dir" (con un archivo .txt dentro, aunque eso todavía no importa). Escribir xyz <TAB>con esta función enumera el directorio y el archivo .txt, pero escribir se xyz d<TAB>expande a xyz dir(bueno, con un espacio después de "dir").

Respuestas:


10

Si observa la función _cd()en / etc / bash_completion , verá que agrega la barra diagonal final y que se completa con la opción -o nospacepara cd .

Puede hacer lo mismo para xyz , pero para tener que verificar por separado si la coincidencia encontrada es un directorio (si es así, agregue una barra diagonal) o un archivo (si es así, agregue espacio). Esto debe hacerse en un bucle for para procesar todas las coincidencias encontradas.

Además, para manejar correctamente las rutas que contienen espacios, debe establecer el separador de archivos interno en solo nueva línea y escapar de los espacios. Usar IFS=$'\n'en combinación con printf %qhace que la finalización funcione con casi todos los personajes. 1 Se debe tener especial cuidado para no escapar del espacio posterior.

Lo siguiente debería funcionar:

_xyz ()
{
    local IFS=$'\n'
    local LASTCHAR=' '

    COMPREPLY=($(compgen -o plusdirs -f -X '!*.txt' \
        -- "${COMP_WORDS[COMP_CWORD]}"))

    if [ ${#COMPREPLY[@]} = 1 ]; then
        [ -d "$COMPREPLY" ] && LASTCHAR=/
        COMPREPLY=$(printf %q%s "$COMPREPLY" "$LASTCHAR")
    else
        for ((i=0; i < ${#COMPREPLY[@]}; i++)); do
            [ -d "${COMPREPLY[$i]}" ] && COMPREPLY[$i]=${COMPREPLY[$i]}/
        done
    fi

    return 0
}

complete -o nospace -F _xyz xyz

1 El carácter de nueva línea es la excepción obvia aquí, ya que es un separador de archivos interno.


Esto funciona muy bien (aunque es una pena que no esté integrado). ¡Gracias!
Rob I

1
¿Alguna razón para no usar "$ 2" en lugar de "$ {COMP_WORDS [COMP_CWORD]}"?
Edward Falk el

3

Creo que esta solución simple funciona en eso:

  1. Coincide con directorios y archivos que terminan en .txt
  2. Maneja espacios en los nombres de archivo
  3. Agrega una barra al final de la finalización de la carpeta sin espacio final
  4. Agrega espacio al final de una coincidencia de finalización de archivo

La clave estaba pasando -o filenamespara completar. Esto se probó en GNU bash 3.2.25 en RHEL 5.3 y GNU bash 4.3.18 en osx

_xyz()
{
  local cur=${COMP_WORDS[COMP_CWORD]}

  local IFS=$'\n'
  COMPREPLY=( $( compgen -o plusdirs  -f -X '!*.txt' -- $cur ) )
}

complete -o filenames -F _xyz xyz

Sí, eso parece funcionar muy bien. Mucho más simple, ¡gracias por entender el argumento importante!
Rob I
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.