www una totales todos permisos modificar los darle dar como carpetas carpeta bash autocomplete bash-completion

bash - una - permisos terminal ubuntu



Obtener compgen para incluir barras en los directorios al buscar archivos (5)

Me gustaría obtener el siguiente comportamiento de mi finalización personalizada

Dado

$ mkdir foo $ touch foo faz/bar faz/baz

Me gustaría obtener esto

$ foo -u <tab><tab> => foo faz/ $ foo -u fa<tab><tab> => foo -u faz/ $ foo -u faz/<tab><tab> => bar baz

Supuse que compgen -ff generaría foo faz/ , pero produce foo faz lo que no me ayuda mucho.

¿Necesito postprocesar el resultado o hay alguna combinación mágica de opciones para compgen que funcione?


Tuve el mismo problema. Aquí está la solución que estoy usando:

  1. Registre la función de finalización con -o default , por ejemplo, complete -o default -F _my_completion .
  2. Cuando desee completar un nombre de archivo, simplemente configure COMPREPLY=() y deje que Readline se haga cargo (eso es lo que hace el -o default ).

Existe un problema potencial con esto: es posible que esté utilizando COMPREPLY=() para denegar la finalización donde no sea apropiado, y eso ya no funcionará. En Bash 4.0 y compopt superiores, puedes compopt esto usando el compopt siguiente manera:

  1. En la parte superior de su función de finalización, siempre ejecute compopt +o default . Esto desactiva la finalización del nombre de archivo de Readline cuando COMPREPLY está vacío.
  2. Cuando desee completar un nombre de archivo, use compopt -o default; COMPREPLY=() compopt -o default; COMPREPLY=() . En otras palabras, habilite la finalización del nombre de archivo de Readline solo cuando lo necesite.

No he encontrado una solución completa para Bash anterior a 4.0, pero tengo algo que funciona lo suficientemente bien para mí. Puedo describir esto si a alguien realmente le importa, pero espero que estas versiones antiguas de Bash pronto se salgan del uso común de todos modos.


Esto funcionó para mí:

_my_completion() { compopt -o nospace COMPREPLY=( $(compgen -S "/" -d "${COMP_WORDS[$COMP_CWORD]}") ) } complete -F _my_completion foo


Si desea poder continuar usando la pestaña después de que se completó el directorio; esta es la solución completa:

cur=${COMP_WORDS[COMP_CWORD]} COMPREPLY=( $(compgen -S "/" -d -- ${cur}) ); compopt -o nospace;

primero agregue una barra inclinada a la respuesta. Luego ejecute el compopt -o nospace para eliminar el espacio final. Ahora, si solo tiene foo/bar en su directorio, ¡dos pestañas bastarán para escribirlas!


Las respuestas anteriores requieren bash 4 o no se pueden componer con otras terminaciones basadas en compgen para el mismo comando. La siguiente solución no requiere componentes (por lo que funciona con bash 3.2) y es composable (por ejemplo, puede agregar filtros adicionales para que coincidan con ciertas extensiones de archivos). Salte al final si es impaciente.

Puede probar compgen ejecutándolo directamente en la línea de comando:

compgen -f -- $cur

donde $cur es la palabra que habría tipeado hasta ahora durante la finalización de tabulación interactiva.

Si estoy en un directorio que tiene un archivo afile.py y un subdirectorio adir , y cur=a , el comando anterior muestra lo siguiente:

$ cur=a $ compgen -f -- $cur adir afile

Tenga en cuenta que compgen -f muestra archivos y directorios. compgen -d muestra solo directorios:

$ compgen -d -- $cur adir

Agregar -S / agregará una barra inclinada a cada resultado:

$ compgen -d -S / -- $cur adir/

Ahora, podemos intentar enumerar todos los archivos y todos los directorios:

$ compgen -f -- $cur; compgen -d -S / -- $cur adir afile.py adir/

Tenga en cuenta que no hay nada que le impida llamar a compgen más de una vez. En tu script de finalización lo usarías así:

cur=${COMP_WORDS[COMP_CWORD]} COMPREPLY=( $(compgen -f -- "$cur"; compgen -d -S / -- "$cur") )

Desafortunadamente, esto todavía no nos da el comportamiento que queremos, porque si escribe ad<TAB> tiene dos terminaciones posibles: adir y adir/ . Por lo tanto, ad<TAB> se completará en adir , en cuyo punto aún tendrá que escribir el / para eliminar la ambigüedad.

Lo que necesitamos ahora es una función que devolverá todos los archivos pero no directorios. Aquí está:

$ grep -v -F -f <(compgen -d -P ''^'' -S ''$'' -- "$cur") / > <(compgen -f -P ''^'' -S ''$'' -- "$cur") | > sed -e ''s/^/^//'' -e ''s//$$//'' afile.py

Vamos a descomponerlo:

  • grep -f file1 file2 significa que muestra las líneas en el archivo2 que coinciden con cualquiera de los patrones del archivo1.
  • -F significa que los patrones en el archivo1 deben coincidir exactamente (como una subcadena); no son expresiones regulares.
  • -v significa invierta la coincidencia: solo muestra las líneas que no están en el archivo1.
  • <(...) es la sustitución del proceso bash. Le permite ejecutar cualquier comando en los lugares donde se espera un archivo.

Entonces le estamos diciendo a grep: aquí hay una lista de archivos: elimine todos los que coincidan con esta lista de directorios.

$ grep -v -F -f <(compgen -d -P ''^'' -S ''$'' -- "$cur") / > <(compgen -f -P ''^'' -S ''$'' -- "$cur") ^afile.py$

He añadido marcadores de inicio y final con comp de -P ^ y -S ''$'' porque grep''s -F hace coincidir las subcadenas y no queremos eliminar un nombre de archivo como a-file-with-adir-in-the-middle solo porque su parte central coincidía con el nombre de un directorio. Una vez que tenemos la lista de archivos, eliminamos esos marcadores con sed.

Ahora podemos escribir una función que hace lo que queremos:

# Returns filenames and directories, appending a slash to directory names. _mycmd_compgen_filenames() { local cur="$1" # Files, excluding directories: grep -v -F -f <(compgen -d -P ^ -S ''$'' -- "$cur") / <(compgen -f -P ^ -S ''$'' -- "$cur") | sed -e ''s/^/^//'' -e ''s//$$/ /'' # Directories: compgen -d -S / -- "$cur" }

Lo usas así:

_mycmd_complete() { local cur=${COMP_WORDS[COMP_CWORD]} COMPREPLY=( $(_mycmd_compgen_filenames "$cur") ) } complete -o nospace -F _mycmd_complete mycmd

Tenga en cuenta que -o nospace significa que no obtiene un espacio después de / . Para archivos normales agregamos un espacio al final con sed.

Una cosa buena de tener esto en una función separada es que es fácil de probar. Por ejemplo, aquí hay una prueba automatizada:

diff -u <(printf "afile.py /nadir//n") <(_mycmd_compgen_filenames "a") / || { echo "error: unexpected completions"; exit 1; }


Este comportamiento está influenciado por los mark-directories variable readline (y los mark-directories mark-symlinked-directories relacionados). Creo que la variable debe configurarse para complete para imprimir una barra inclinada en los nombres de los directorios (el valor predeterminado en bash v3.00.16). Aparentemente, el comportamiento relacionado de compgen no compgen una barra oblicua a los nombres de directorio: - /

Establezca el valor de mark-directories alternativamente en on y off y luego vuelva a intentar su prueba:

bind ''set mark-directories on'' bind ''set mark-directories off''

Para que el cambio sea permanente para futuras invocaciones de bash , agregue lo siguiente a su archivo INPUTRC, comúnmente ~/.inputrc : -

$if Bash # append ''/'' to dirnames set mark-directories on $endif

La sugerencia para establecer las variables de lectura en el shell actual se encontró aquí: https://unix.stackexchange.com/a/27545 . No determiné cómo probar el valor actual de una variable de lectura.

Ideas adicionales

Quizás solo interés académico ...

Cree una lista de nombres de directorio solamente y añada una barra inclinada: -

compgen -d -S / f