Tab-Vervollständigung in Bash für einige Verzeichnisse deaktivieren, die eine große Anzahl von Dateien enthalten?

Tab-Vervollständigung in Bash für einige Verzeichnisse deaktivieren, die eine große Anzahl von Dateien enthalten?

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 (kann über eine Minute dauern), bis Dateien angezeigt werden, die dem Muster entsprechen, und dieses Verhalten ärgert mich ziemlich.

Meine Verzeichnisstruktur ist beispielsweise:

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

Da ich die Vervollständigung immer noch liebe, gibt es eine Möglichkeit, diese Funktion nur im data/Verzeichnis zu deaktivieren? Zum Beispiel durch Festlegen des Timeouts auf 5 Sekunden oder durch ein Skript, das die Vervollständigung automatisch deaktiviert, wenn man sich in einem bestimmten Verzeichnis befindet?

Antwort1

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

Die einfachste Möglichkeit ist, sie auf Befehlsbasis auszuführen. Sie ist etwas flexibler als FIGNORE. Sie können Folgendes tun:

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

Dies weist die Autovervollständigung an, dass die Vervollständigung vifür Dateien erfolgt und Muster entfernt werden, die dem -XFilter entsprechen. Der Nachteil besteht darin, dass das Muster nicht normalisiert ist ../dataund daher Variationen nicht übereinstimmen.

Die nächstbeste Lösung könnte eine benutzerdefinierte PROMPT_COMMANDFunktion sein:

# 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

Dasdeaktiviert die Vervollständigung(komplett) wenn Sie sich im Verzeichnis befinden,AberEs deaktiviert es für jeden Pfad, nicht nur für Dateien in diesem Verzeichnis.

Es wäre grundsätzlich sinnvoller, dies für definierte Pfade selektiv zu deaktivieren, aber ich glaube, die einzige Möglichkeit besteht darin, eine Standardvervollständigungsfunktion (Bash-4.1 und höher mit complete -D) zu verwenden und viel herumzuspielen.

Dassollenfür Sie arbeiten, aber esMaihaben unbeabsichtigte Nebenwirkungen (d. h. in manchen Fällen Änderungen des erwarteten Abschlusses):

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 für die Vervollständigung von vi, bei Bedarf können weitere Befehle hinzugefügt werden. Es sollte die Vervollständigung für Dateien in den genannten Verzeichnissen unabhängig von Pfad oder Arbeitsverzeichnis stoppen.

Ich glaube, der allgemeine Ansatz complete -Dbesteht darin, dynamisch Vervollständigungsfunktionen für jeden Befehl hinzuzufügen, sobald er auftritt. Möglicherweise muss auch Folgendes hinzugefügt werden complete -E(Vervollständigung des Befehlsnamens, wenn der Eingabepuffer leer ist).


Aktualisieren Hier ist eine Hybridversion der PROMPT_COMMANDLösungen mit der Vervollständigungsfunktion, die meiner Meinung nach etwas einfacher zu verstehen und zu hacken ist:

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 setzt die nocompleteVariable, wenn Sie eines der konfigurierten Verzeichnisse betreten. Das geänderte Vervollständigungsverhalten tritt nur dann ein, wenn die Variable nicht leer ist.Undnur wenn Sie versuchen, die Vervollständigung aus einer leeren Zeichenfolge vorzunehmen, sodass die Vervollständigung von Teilnamen möglich ist (entfernen Sie die -z "$cur"Bedingung, um die Vervollständigung vollständig zu verhindern). Kommentieren Sie die beiden printfZeilen für den stillen Betrieb aus.

.noautocompleteZu den weiteren Optionen gehören eine Flag-Datei pro Verzeichnis , die Sie touchnach Bedarf in einem Verzeichnis speichern können, und die Schätzung der Verzeichnisgröße mithilfe von GNU stat. Sie können eine oder alle dieser drei Optionen verwenden.

(Die statMethodeist nur eine Vermutung, die gemeldete Verzeichnisgröße wächst mit seinem Inhalt, es handelt sich um eine „Hochwassermarke“, die normalerweise nicht schrumpft, wenn Dateien ohne administratives Eingreifen gelöscht werden. Es ist billiger, als den tatsächlichen Inhalt eines möglicherweise großen Verzeichnisses zu ermitteln. Das genaue Verhalten und die Erhöhung pro Datei hängen vom zugrunde liegenden Dateisystem ab. Ich halte es zumindest auf Linux-Ext2/3/4-Systemen für einen zuverlässigen Indikator.)

bash fügt ein zusätzliches Leerzeichen hinzu, selbst wenn eine leere Vervollständigung zurückgegeben wird (dies tritt nur bei der Vervollständigung am Ende einer Zeile auf). Sie können -o nospacedem completeBefehl etwas hinzufügen, um dies zu verhindern.

Ein verbleibender Kritikpunkt ist, dass die Standardvervollständigung wieder aktiviert wird, wenn Sie den Cursor an den Anfang eines Tokens zurücksetzen und die Tabulatortaste drücken. Betrachten Sie es als eine Funktion ;-)

(Oder Sie können ein bisschen herumprobieren, ${COMP_LINE:$COMP_POINT-1:1}wenn Sie es mit der Technik übertreiben möchten, aber meiner Erfahrung nach gelingt es Bash selbst nicht, die Vervollständigungsvariablen zuverlässig festzulegen, wenn Sie mitten in einem Befehl einen Schritt zurückgehen und die Vervollständigung versuchen.)

Antwort2

Wenn Ihr dataVerzeichnis Dateien mit einer bestimmten Endung enthält,z.B .out, dann können Sie Ihre bashVariable FIGNOREauf setzen ".out"und diese werden ignoriert. Obwohl es möglich ist, auch 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

Legen Sie die bashVariable fest:
$ FIGNORE=".json"

Erstellen Sie die Testdatei:
$ touch test.out

Testen bei5.000Dateien:

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

Es tritt keine Verzögerung zwischen dem vi und dem einzelnen tabdavor test.outauf.

Testen bei50.000Dateien:

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

Beim Erscheinen einer einzelnen Registerkarte kommt es zu einer Verzögerung von einem Sekundenbruchteil test.out.

Testen bei200.000Dateien:

$ 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 dem tabvorherigen Single test.out.

Verweise:

Auszug aus 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:~".

Antwort3

Ich schätze, das ist, was Sie wollen (habe etwas Code von @mr.spuratic ausgeliehen)

Beachten Sie, dass dies Sie nicht daran hindert, die Tabulatortaste zu drücken und dabei den vollständigen Pfad einzugeben (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
}  

verwandte Informationen