Désactiver la complétion des onglets dans Bash pour certains répertoires contenant un grand nombre de fichiers?

19

Je travaille avec un grand nombre de fichiers que je garde dans un répertoire. Chaque fois que je vais dans ce répertoire et que j'appuie accidentellement Tabdeux fois, cela prend trop de temps (peut-être plus d'une minute) pour afficher les fichiers qui correspondent au modèle, et je suis assez ennuyé par ce comportement.

Par exemple, ma structure de répertoire est:

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

Puisque j'aime toujours l'achèvement, est-il possible de désactiver cette fonctionnalité uniquement dans le data/répertoire? Comme définir un délai d'expiration de 5 secondes, ou un script qui éteint automatiquement l'achèvement à l'intérieur d'un répertoire spécifique?

neizod
la source
Souhaitez-vous passer à zsh à ce sujet? (Je ne sais pas si c'est possible en bash. Je sais que c'est possible en zsh, mais ce n'est peut-être pas facile, je n'ai pas vérifié.)
Gilles 'SO- arrête d'être méchant'

Réponses:

9

Ce n'est pas parfait, mais encore une fois, l'achèvement de bash est assez difficile ...

La manière la plus simple est sur une base par commande, elle est légèrement plus flexible que FIGNORE, vous pouvez le faire:

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

Cela indique à la saisie semi-automatique que la complétion viest pour les fichiers et à supprimer les modèles correspondant au -Xfiltre. L'inconvénient est que le modèle n'est pas normalisé, donc les ../datavariations ne correspondent pas.

La prochaine meilleure chose pourrait être une PROMPT_COMMANDfonction personnalisée :

# 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

Cela désactive l'achèvement (complètement) lorsque vous êtes dans le répertoire, mais il le désactive pour chaque chemin, pas seulement pour les fichiers de ce répertoire.

Il serait plus généralement utile de désactiver cela de manière sélective pour des chemins définis, mais je crois que la seule façon est d'utiliser une fonction de complétion par défaut (bash-4.1 et ultérieur avec complete -D) et beaucoup de déconner.

Cela devrait fonctionner pour vous, mais cela peut avoir des effets secondaires imprévus (c'est-à-dire des changements à la fin attendue dans certains cas):

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

Cela fonctionne pour l'achèvement de vi, d'autres commandes peuvent être ajoutées au besoin. Il doit arrêter l'achèvement des fichiers dans les répertoires nommés, quel que soit le chemin ou le répertoire de travail.

Je pense que l'approche générale complete -Dconsiste à ajouter dynamiquement des fonctions d'achèvement pour chaque commande au fur et à mesure qu'elle se produit. Il peut également être nécessaire d'ajouter complete -E(achèvement du nom de la commande lorsque le tampon d'entrée est vide).


Mise à jour Voici une version hybride des PROMPT_COMMANDsolutions de fonction et de complétion, c'est un peu plus facile à comprendre et à pirater je pense:

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

Cette fonction d'invite définit la nocompletevariable lorsque vous entrez dans l'un des répertoires configurés. Le comportement de complétion modifié n'intervient que lorsque cette variable n'est pas vide et uniquement lorsque vous essayez de terminer à partir d'une chaîne vide, permettant ainsi la complétion des noms partiels (supprimez la -z "$cur"condition pour empêcher complètement la complétion). Commentez les deux printflignes pour un fonctionnement silencieux.

Les autres options incluent un .noautocompletefichier indicateur par répertoire que vous pouvez touchdans un répertoire selon vos besoins; et deviner la taille du répertoire en utilisant GNU stat. Vous pouvez utiliser l'une ou l'ensemble de ces trois options.

(La statméthode n'est qu'une supposition , la taille du répertoire signalé augmente avec son contenu, c'est un «high water mark» qui ne rétrécit généralement pas lorsque les fichiers sont supprimés sans intervention administrative. C'est moins cher que de déterminer le contenu réel d'un fichier potentiellement volumineux Le comportement précis et l'incrémentation par fichier dépendent du système de fichiers sous-jacent. Je le trouve au moins un indicateur fiable sur les systèmes linux ext2 / 3/4.)

bash ajoute un espace supplémentaire même quand un achèvement vide est retourné (cela ne se produit que lors de l'achèvement à la fin d'une ligne). Vous pouvez ajouter -o nospaceà la completecommande pour éviter cela.

Un inconvénient restant est que si vous sauvegardez le curseur au début d'un jeton et appuyez sur l'onglet, l'achèvement par défaut se déclenchera à nouveau. Considérez cela comme une fonctionnalité ;-)

(Ou vous pouvez essayer ${COMP_LINE:$COMP_POINT-1:1}si vous aimez la sur-ingénierie, mais je trouve que bash lui-même ne parvient pas à définir les variables d'achèvement de manière fiable lorsque vous sauvegardez et tentez l'achèvement au milieu d'une commande.)

Mr Spuratic
la source
6

Si votre datarépertoire contient des fichiers avec un suffixe spécifique, par exemple .out , vous pouvez définir votre bashvariable FIGNOREsur ".out"et ceux-ci seront ignorés. Bien qu'il soit également possible d'utiliser des noms de répertoire, cela n'aide pas les noms de répertoire de travail actuels.

Exemple:

Créez des milliers de fichiers de test de 100 Ko sur l'hôte physique avec les disques durs RAID 1:

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

Définissez la bashvariable:
$ FIGNORE=".json"

Créez le fichier de test:
$ touch test.out

Testez à 5000 fichiers:

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

Aucun délai entre le vi et le single tabavant test.outn'apparaît.

Testez à 50 000 fichiers:

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

Un seul onglet crée une fraction de seconde de délai avant d’ test.outapparaître.

Testez à 200 000 fichiers:

$ 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

Un délai de 1 seconde entre le vi et le single tabavant test.outapparaît.

Les références:

Extrait de l'homme 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
la source
1
n'aidera pas, essayez de créer un répertoire avec environ ~ 100 Ko de fichiers. alors je le fais $ vi <tab><tab>, ça prend beaucoup de temps de toute façon. : \
neizod
toujours pas résoudre le problème, supposons que j'ai 100 000 .jsonfichiers et un seul fichier texte nommé foo.txt. allumant FIGNORE='.json', je tape $ vi <tab>et dois encore attendre une demi-minute pour que cela me complète $ vi foo.txt. --- scénario réel, je ne vais pas éditer, ni mélanger les types de fichiers dans ce répertoire, mais si j'allume FIGNOREet (accidentellement) j'appuie sur tab, mon clavier se fige pendant longtemps sans aucune allusion à quelque chose comme ça Display all 188275 possibilities? (y or n), qui me dit que je peux récupérer mon clavier.
neizod
@neizod - Désolé, j'ai essayé de nombreux scénarios pour que FIGNORE fonctionne au niveau du répertoire, en vain. Je suppose qu'une solution personnalisée est requise, ou vous désactivez simplement l'achèvement de bash sur cet hôte, ou essayez la solution zsh détaillée sur notre site frère mentionné par Gilles.
geedoubleya
Désolé, c'est seulement 1 seconde sur votre système, c'est environ une demi-minute sur mon système. Je suppose donc que j'irais acheter un nouveau PC pour faire fonctionner cette solution.
neizod
0

Je suppose que c'est ce que vous voulez (emprunté du code à @ mr.spuratic)

Notez que cela ne vous empêche pas d'appuyer sur TAB en utilisant le chemin complet (par exemple 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
la source
N'oubliez pas pushd/ popd. Comme cela n'utilise qu'un tableau régulier, pas un tableau associatif, il fonctionnera également dans bash 3.x.
mr.spuratic