Deaktivieren Sie die Tab-Vervollständigung in Bash für ein Verzeichnis, das eine große Anzahl von Dateien enthält?

19

Ich arbeite mit einer großen Anzahl von Dateien, die ich in einem Verzeichnis aufbewahre. Jedes Mal, wenn ich in dieses Verzeichnis gehe und versehentlich Tabzweimal drücke , dauert es zu lange (möglicherweise länger als eine Minute), um Dateien anzuzeigen, die dem Muster entsprechen, und ich ärgere mich ziemlich über dieses Verhalten.

Zum Beispiel ist meine Verzeichnisstruktur:

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

Da ich die Fertigstellung immer noch liebe, gibt es trotzdem die Möglichkeit, diese Funktion nur im data/Verzeichnis zu deaktivieren ? Möchten Sie eine Zeitüberschreitung von 5 Sekunden festlegen oder ein Skript, das die Fertigstellung automatisch abschaltet, wenn Sie sich in einem bestimmten Verzeichnis befinden?

neizod
quelle
Würden Sie diesbezüglich zu zsh wechseln? (Ich weiß nicht, ob es in bash möglich ist. Ich weiß, dass es in zsh möglich ist, aber es könnte nicht einfach sein, ich habe es nicht überprüft.)
Gilles 'SO - hör auf, böse zu sein'

Antworten:

9

Das ist nicht perfekt, aber die Bash-Vervollständigung ist eine ziemlich knifflige Sache ...

Der einfachste Weg ist auf Befehlsbasis. Er ist etwas flexibler als FIGNORESie es tun können:

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

Dies weist die automatische Vervollständigung an, dass die Vervollständigung für viDateien gilt, und das Entfernen von Mustern, die mit dem -XFilter übereinstimmen . Der Nachteil ist, dass das Muster nicht normalisiert ist ../dataund die Variationen nicht übereinstimmen.

Das nächstbeste ist möglicherweise eine benutzerdefinierte PROMPT_COMMANDFunktion:

# 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

Dadurch wird die Vervollständigung (vollständig) deaktiviert, wenn Sie sich im Verzeichnis befinden. Sie wird jedoch für jeden Pfad deaktiviert, nicht nur für Dateien in diesem Verzeichnis.

Es wäre allgemeiner sinnvoll, dies für definierte Pfade selektiv zu deaktivieren, aber ich glaube, der einzige Weg ist die Verwendung einer Standard-Vervollständigungsfunktion (bash-4.1 und höher mit complete -D) und viel Herumspielen.

Dies sollte für Sie funktionieren, kann jedoch unbeabsichtigte Nebenwirkungen haben (z. B. Änderungen an der erwarteten Fertigstellung in einigen Fällen):

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

Dies funktioniert zur Vervollständigung vi, andere Befehle können nach Bedarf hinzugefügt werden. Die Fertigstellung der Dateien in den angegebenen Verzeichnissen sollte unabhängig von Pfad oder Arbeitsverzeichnis beendet sein.

Ich glaube, dass der allgemeine Ansatz darin complete -Dbesteht, Vervollständigungsfunktionen für jeden Befehl dynamisch hinzuzufügen, wenn er angetroffen wird. Möglicherweise müssen Sie auch hinzufügen complete -E(Vervollständigung des Befehlsnamens, wenn der Eingabepuffer leer ist).


Update Hier ist eine Hybridversion der PROMPT_COMMANDund Vervollständigungsfunktion Lösungen, es ist ein wenig einfacher zu verstehen und zu hacken, denke ich:

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

Diese Eingabeaufforderungsfunktion legt die nocompleteVariable fest, wenn Sie eines der konfigurierten Verzeichnisse eingeben. Das geänderte Vervollständigungsverhalten tritt nur ein, wenn diese Variable nicht leer ist und wenn Sie versuchen, die Vervollständigung aus einer leeren Zeichenfolge vorzunehmen, wodurch die Vervollständigung von Teilnamen ermöglicht wird (entfernen Sie die -z "$cur"Bedingung, um die Vervollständigung insgesamt zu verhindern). Kommentieren Sie die beiden printfZeilen für den stillen Betrieb aus.

Andere Optionen umfassen eine verzeichnisbezogene .noautocompleteFlag-Datei, die Sie nach Bedarf touchin einem Verzeichnis ablegen können. und Erraten der Verzeichnisgröße mit GNU stat. Sie können eine oder alle dieser drei Optionen verwenden.

(Die statMethode ist nur eine Vermutung , die gemeldete Verzeichnisgröße wächst mit dem Inhalt, es ist eine "High Water Mark", die normalerweise nicht verkleinert wird, wenn Dateien ohne administrativen Eingriff gelöscht werden. Sie ist billiger als die Ermittlung des tatsächlichen Inhalts einer potenziell großen Datei Das genaue Verhalten und das Inkrementieren pro Datei hängen vom zugrunde liegenden Dateisystem ab. Ich finde es zumindest auf Linux Ext2 / 3/4-Systemen ein zuverlässiger Indikator.)

Bash fügt ein zusätzliches Leerzeichen hinzu, auch wenn ein leerer Abschluss zurückgegeben wird (dies tritt nur auf, wenn der Abschluss am Ende einer Zeile erfolgt). Sie können -o nospaceden completeBefehl erweitern, um dies zu verhindern.

Ein verbleibender Haken ist, dass, wenn Sie den Cursor an den Anfang eines Tokens bewegen und auf die Registerkarte klicken, die Standardvervollständigung erneut aktiviert wird. Betrachten Sie es als ein Feature ;-)

(Oder Sie könnten herumfummeln, ${COMP_LINE:$COMP_POINT-1:1}wenn Sie Überplanung mögen, aber ich finde, dass bash selbst die Beendigungsvariablen nicht zuverlässig festlegt, wenn Sie mitten in einem Befehl eine Sicherung durchführen und versuchen, die Beendigung durchzuführen .)

mr.spuratic
quelle
6

Wenn Ihr dataVerzeichnis z. B. Dateien mit einem bestimmten Suffix enthält, .outkönnen Sie Ihre bashVariable FIGNOREauf setzen, ".out"und diese werden ignoriert. Obwohl es auch möglich ist, Verzeichnisnamen zu verwenden, hilft dies bei aktuellen Arbeitsverzeichnisnamen nicht.

Beispiel:

Erstellen Sie Tausende von 100-KB-Testdateien auf einem physischen Host mit RAID 1-Festplatten:

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

Setze die bashVariable:
$ FIGNORE=".json"

Erstellen Sie die Testdatei:
$ touch test.out

Test bei 5.000 Dateien:

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

Es tritt keine Verzögerung zwischen dem vi und dem single tabbefore test.outauf.

Test bei 50.000 Dateien:

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

Einzelne Registerkarte erstellt einen Bruchteil einer Sekunde Verzögerung, bevor test.outangezeigt wird.

Test bei 200.000 Dateien:

$ 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

Verzögerung von 1 Sekunde zwischen dem vi und Einzel tabvor test.outerscheint.

Verweise:

Hervorragend von der 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
quelle
1
wird nicht helfen, versuchen Sie, Verzeichnis mit ca. 100.000 Dateien zu erstellen. dann brauche ich $ vi <tab><tab>sowieso viel Zeit. : \
neizod
Das Problem lässt sich immer noch nicht lösen. Angenommen, ich habe 100.000 .jsonDateien und eine einzelne Textdatei mit dem Namen foo.txt. Beim Einschalten FIGNORE='.json'tippe ich $ vi <tab>und muss noch eine halbe Minute warten, damit es mich komplett macht $ vi foo.txt. --- reales Szenario Ich werde keine Dateien in diesem Verzeichnis bearbeiten oder mischen, aber wenn ich sie einschalte FIGNOREund (versehentlich) die Tabulatortaste drücke, friert meine Tastatur für eine lange Zeit ein, ohne einen Hinweis auf etwas Display all 188275 possibilities? (y or n), das es mir sagt dass ich meine tastatur wieder haben kann.
Neizod
@neizod - Entschuldigung, ich habe viele Szenarien ausprobiert, um FIGNORE auf Verzeichnisebene zum Laufen zu bringen, ohne Erfolg. Ich vermute, dass eine benutzerdefinierte Lösung erforderlich ist, oder Sie deaktivieren einfach die Bash-Vervollständigung auf diesem Host oder versuchen die auf unserer Schwestersite, Gilles, beschriebene zsh-Lösung.
Geedoubleya
Entschuldigung, es ist nur 1 Sekunde auf Ihrem System, es ist ungefähr eine halbe Minute auf meinem System. Ich würde mir also einen neuen PC kaufen, damit diese Lösung funktioniert.
Neizod
0

Schätze, das ist, was du willst (Code von @ mr.spuratic ausgeliehen)

Beachten Sie, dass Sie dadurch nicht daran gehindert werden, die Tabulatortaste mit vollem Pfad zu drücken (z. B. 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
quelle
Vergiss nicht pushd/ popd. Da dies nur ein reguläres Array verwendet, kein assoziatives Array, funktioniert es auch in Bash 3.x.
mr.spuratic