¿Deshabilitar la finalización de pestañas en Bash para algún directorio que contiene una gran cantidad de archivos?

19

Estoy trabajando con una gran cantidad de archivos, que guardo en un directorio. Cada vez que entro en ese directorio y presiono accidentalmente Tabdos veces, toma demasiado tiempo (puede ser más de un minuto) mostrar archivos que coinciden con el patrón, y estoy bastante molesto con este comportamiento.

Por ejemplo, mi estructura de directorio es:

my-project/
├── docs/
├── data/        <---- contains around 200k files.
└── analyser/

Como todavía me encanta completar, ¿hay alguna forma de deshabilitar esta función solo en el data/directorio? ¿Como establecer un tiempo de espera de 5 segundos o un script que apaga automáticamente la finalización cuando está dentro de un directorio específico?

neizod
fuente
¿Cambiarías a zsh por esto? (No sé si es posible en bash Sé que es posible en zsh, pero podría no ser fácil, no lo he comprobado..)
Gilles 'SO- estar parada mal'

Respuestas:

9

Esto no es perfecto, pero una vez más la finalización de bash es bastante difícil ...

La forma más simple es por orden, es un poco más flexible que FIGNORE, puedes hacer:

 complete -f -X "/myproject/data/*" vi

Esto le indica al autocompletado que la finalización de vies para archivos y para eliminar patrones que coincidan con el -Xfiltro. La desventaja es que el patrón no está normalizado, así que../data variaciones no coincidirán.

La siguiente mejor opción podría ser una PROMPT_COMMANDfunción personalizada :

# associative arrays of non-autocomplete directories
declare -A noacdirs=([/myproject/data]=1 )

function _myprompt {
    [[ -n "${noacdirs[$PWD]}" ]] && {
       echo autocomplete off
       bind 'set disable-completion on'
    } || {
       echo autocomplete on
       bind 'set disable-completion off'
    }
} 

PROMPT_COMMAND=_myprompt

Esto desactiva la finalización (completamente) cuando está en el directorio, pero lo desactiva para cada ruta, no solo para los archivos en ese directorio.

Sería más útil en general deshabilitar selectivamente esto para rutas definidas, pero creo que la única forma es usar una función de finalización predeterminada (bash-4.1 y posterior con complete -D ) y un montón de problemas.

Esto debería funcionar para usted, pero puede tener efectos secundarios no deseados (es decir, cambios en la finalización esperada en algunos casos):

declare -A noacdirs=([/myproject/data]=1 )

_xcomplete() {
    local cur=${COMP_WORDS[COMP_CWORD]} # the current token

    name=$(readlink -f "${cur:-./}")  # poor man's path canonify
    dirname=$(dirname "$name/.")

    [[ -n "${noacdirs[$dirname]}" ]] && {
        COMPREPLY=( "" )   # dummy to prevent completion
        return
    }
    # let default kick in
    COMPREPLY=()
}

complete -o bashdefault -o default -F _xcomplete vi

Esto funciona para completar vi, se pueden agregar otros comandos según sea necesario. Debe detener la finalización de los archivos en los directorios nombrados, independientemente de la ruta o el directorio de trabajo.

Creo que el enfoque general con complete -Des agregar dinámicamente funciones de finalización para cada comando a medida que se encuentra. También podría ser necesario agregar complete -E(completar el nombre del comando cuando el búfer de entrada está vacío).


Actualización Aquí hay una versión híbrida de las PROMPT_COMMANDsoluciones de función de finalización, creo que es un poco más fácil de entender y piratear:

declare -A noacdirs=([/myproject/data]=1 [/project2/bigdata]=1)

_xcomplete() {
    local cmd=${COMP_WORDS[0]}
    local cur=${COMP_WORDS[COMP_CWORD]} # the current token

    [[ -z "$cur" && -n "$nocomplete" ]] && {
        printf "\n(restricted completion for $cmd in $nocomplete)\n"  
        printf "$PS2 $COMP_LINE"
        COMPREPLY=( "" )   # dummy to prevent completion
        return
    }
    COMPREPLY=()       # let default kick in
}

function _myprompt {
    nocomplete=
    # uncomment next line for hard-coded list of directories
    [[ -n "${noacdirs[$PWD]}" ]] && nocomplete=$PWD
    # uncomment next line for per-directory ".noautocomplete"
    # [[ -f ./.noautocomplete ]] && nocomplete=$PWD
    # uncomment next line for size-based guessing of large directories
    # [[ $(stat -c %s .) -gt 512*1024 ]] && nocomplete=$PWD
} 

PROMPT_COMMAND=_myprompt
complete -o bashdefault -o default -F _xcomplete vi cp scp diff

Esta función de solicitud establece la nocompletevariable cuando ingresa uno de los directorios configurados. El comportamiento de finalización modificado solo se activa cuando esa variable no está en blanco y solo cuando intenta completar desde una cadena vacía, lo que permite completar nombres parciales (elimine la -z "$cur"condición para evitar que se complete por completo). Comenta los dosprintf líneas para una operación silenciosa.

Otras opciones incluyen un .noautocompletearchivo de marca por directorio que puede touchen un directorio según sea necesario; y adivinar el tamaño del directorio usando GNUstat . Puede usar cualquiera o todas esas tres opciones.

(El statmétodo es solo una suposición , el tamaño del directorio reportado crece con su contenido, es una "marca de límite" que generalmente no se reducirá cuando los archivos se eliminen sin alguna intervención administrativa. Es más barato que determinar el contenido real de un archivo potencialmente grande directorio. El comportamiento preciso y el incremento por archivo dependen del sistema de archivos subyacente. Me parece un indicador confiable en los sistemas Linux ext2 / 3/4 al menos).

bash agrega un espacio adicional incluso cuando se devuelve una finalización vacía (esto solo ocurre cuando se completa al final de una línea). Puede agregar -o nospaceal completecomando para evitar esto.

Un inconveniente restante es que si retrocede el cursor al comienzo de un token y presiona la pestaña, la finalización predeterminada se activará nuevamente. Considéralo una característica ;-)

(O podría perder el tiempo ${COMP_LINE:$COMP_POINT-1:1}si le gusta la ingeniería excesiva, pero creo que bash en sí mismo no puede establecer las variables de finalización de manera confiable cuando realiza una copia de seguridad e intenta completarla en medio de un comando).

Sr. púrpura
fuente
6

Si el datadirectorio contiene archivos con un sufijo específico, por ejemplo .out , entonces se puede establecer su bashvariable de FIGNOREa ".out"y éstos serán ignoradas. Aunque también es posible usar nombres de directorio, esto no ayuda en los nombres de directorio de trabajo actuales.

Ejemplo:

Cree miles de archivos de prueba de 100 KB en el host físico con discos duros RAID 1:

for i in {1..200000}; do dd if=/dev/urandom bs=102400 count=1 of=file$i.json; done

Establecer la bashvariable:
$ FIGNORE=".json"

Crea el archivo de prueba:
$ touch test.out

Prueba en 5,000 archivos:

$ ls *.json|wc -l
5587
$ vi test.out

No aparece ningún retraso entre vi y single tabantes test.out.

Prueba a 50,000 archivos:

$ ls *.json|wc -l
51854
$ vi test.out

Una sola pestaña crea una fracción de segundo retraso antes de que test.outaparezca.

Prueba a 200,000 archivos:

$ ls *.json|wc -l
bash: /bin/ls: Argument list too long
$ ls | awk 'BEGIN {count=0} /.*.json/ {count++} END {print count}'
200000
$ vi test.out

Retardo de 1 segundo entre la VI y sola tabantes de test.outque aparezca.

Referencias

Sobresalir de man bash

FIGNORE
              A  colon-separated  list  of  suffixes to ignore when performing filename completion (see READLINE below).  A filename whose suffix matches one of the entries in FIGNORE is
              excluded from the list of matched filenames.  A sample value is ".o:~".
geedoubleya
fuente
1
no ayudará, intente crear dir w / aproximadamente ~ 100k archivos. entonces lo hago $ vi <tab><tab>, toma mucho tiempo de todos modos. : \
neizod
Todavía no resuelvo el problema, supongamos que tengo 100k .jsonarchivos y un solo archivo de texto llamado foo.txt. enciendo FIGNORE='.json', escribo $ vi <tab>y todavía necesito esperar medio minuto para que me complete $ vi foo.txt. --- escenario real que no voy a editar, o mezclar tipos de archivos dentro de ese directorio, pero si enciendo FIGNOREy (accidentalmente) presiono la tecla, mi teclado se congelará por un largo tiempo sin una pista de algo como Display all 188275 possibilities? (y or n), lo que me dice que puedo recuperar mi teclado.
neizod
@neizod - Lo siento, intenté muchos escenarios para hacer que FIGNORE funcione en un nivel de directorio, pero fue en vano. Supongo que se requiere una solución personalizada, o simplemente deshabilita la finalización de bash en ese host, o prueba la solución zsh detallada en nuestro sitio hermano mencionado por Gilles.
geedoubleya
Lo siento, es solo 1 segundo en su sistema, es alrededor de medio minuto en mi sistema. Así que supongo que compraría una nueva PC para que esta solución funcione.
neizod
0

Supongo que esto es lo que quieres (tomé prestado un código de @ mr.spuratic)

Tenga en cuenta que esto no le impide presionar TAB con la ruta completa (p. Ej. vim /directory/contains/lots/files<tab><tab>

function cd() {
  builtin cd "$@"
  local NOCOMPLETION=( "/" "/lib" )
  local IS_NOCOMPLETION=0
  for dir in "${NOCOMPLETION[@]}"
  do
    echo $dir
    if [[ $(pwd) == "$dir" ]]
    then
      echo "disable completion"
      bind "set disable-completion on"
      IS_NOCOMPLETION=1
      return
    fi                                                                                                                                                                                              
  done
  if [[ $IS_NOCOMPLETION -eq 0 ]]
  then
    echo "enable completion"
    bind "set disable-completion off"
  fi
}  
yegle
fuente
No te olvides de pushd/ popd. Como esto solo usa una matriz regular, no una matriz asociativa, también funcionará en bash 3.x.
Sr.Spuratic