Wyłączyć uzupełnianie kart w Bash dla niektórych katalogów, które zawierają dużą liczbę plików?

19

Pracuję z dużą liczbą plików, które trzymam w katalogu. Za każdym razem Tab, gdy wchodzę do tego katalogu i przez przypadkowe dwukrotne naciśnięcie , wyświetlenie plików pasujących do wzorca trwa zbyt długo (może to potrwać ponad minutę), a ja jestem zirytowany tym zachowaniem.

Na przykład moja struktura katalogów to:

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

Ponieważ nadal uwielbiam ukończenie, czy w ogóle można wyłączyć tę funkcję tylko w data/katalogu? Jak ustawiony limit czasu na 5 sekund lub skrypt, który automatycznie wyłącza zakończenie, gdy znajduje się w określonym katalogu?

neizod
źródło
Czy przeszedłbyś na Zsh? (Nie wiem, czy jest to możliwe w bash. Wiem, że jest to możliwe w Zsh, ale może to nie być łatwe, nie sprawdziłem.)
SO Gillesa - przestań być zły ”

Odpowiedzi:

9

To nie jest idealne, ale z drugiej strony ukończenie bashu jest dość trudne ...

Najprostszy sposób jest oparty na poleceniach, jest nieco bardziej elastyczny niż FIGNOREmożesz:

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

To instruuje autouzupełnianie, że uzupełnianie vidotyczy plików i usuwania wzorców pasujących do -Xfiltra. Minusem jest to, że wzorzec nie jest znormalizowany, więc ../dataodmiany nie będą pasować.

Kolejną najlepszą rzeczą może być PROMPT_COMMANDfunkcja niestandardowa :

# 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

To zakończenie wyłącza (całkowicie), gdy jesteś w katalogu, ale nie wyłącza go to dla każdej ścieżki tylko pliki znajdujące się w tym katalogu.

Bardziej ogólnie przydałoby się selektywne wyłączanie tego dla zdefiniowanych ścieżek, ale uważam, że jedynym sposobem jest użycie domyślnej funkcji uzupełniania (bash-4.1 i później z complete -D) i dużo bałaganu.

To powinno pracować dla Ciebie, ale może mieć niezamierzone efekty uboczne (czyli zmiany w przewidywanym zakończeniu w niektórych przypadkach):

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

Działa to do zakończenia vi, w razie potrzeby można dodać inne polecenia. Powinno to zatrzymać uzupełnianie plików w nazwanych katalogach niezależnie od ścieżki lub katalogu roboczego.

Uważam, że ogólne podejście complete -Dpolega na dynamicznym dodawaniu funkcji uzupełniania dla każdego polecenia, jakie napotka. Konieczne może być również dodanie complete -E(uzupełnienie nazwy polecenia, gdy bufor wejściowy jest pusty).


Aktualizacja Oto hybrydowa wersja PROMPT_COMMANDrozwiązań funkcji uzupełniania i jest nieco łatwiejsza do zrozumienia i zhakowania, jak sądzę:

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

Ta funkcja pytania ustawia nocompletezmienną po wejściu do jednego ze skonfigurowanych katalogów. Zmodyfikowane zachowanie uzupełniania włącza się tylko wtedy, gdy zmienna nie jest pusta i tylko wtedy, gdy próbujesz uzupełnić z pustego łańcucha, umożliwiając w ten sposób uzupełnienie częściowych nazw (usuń -z "$cur"warunek, aby całkowicie nie dopełnić). Skomentuj dwie printflinie dla cichej pracy.

Inne opcje obejmują .noautocompleteplik flag dla katalogu, który można touchw razie potrzeby w katalogu; i zgadywanie rozmiaru katalogu za pomocą GNU stat. Możesz użyć jednej lub wszystkich tych trzech opcji.

( statMetoda jest tylko zgadywaniem , zgłaszany rozmiar katalogu rośnie wraz z jego zawartością, jest to „wysoki znak wodny”, który zwykle nie zmniejsza się, gdy pliki są usuwane bez interwencji administracyjnej. Jest to tańsze niż określenie rzeczywistej zawartości potencjalnie dużej katalog. Precyzyjne zachowanie i przyrost na plik zależy od bazowego systemu plików. Uważam, że jest to wiarygodny wskaźnik przynajmniej w systemach Linux ext2 / 3/4).

bash dodaje dodatkową spację nawet po zwróceniu pustego zakończenia (dzieje się tak tylko po zakończeniu na końcu linii). Możesz dodać -o nospacedo completepolecenia, aby temu zapobiec.

Pozostałym problemem jest to, że jeśli cofniesz kursor na początek tokena i klikniesz kartę, domyślne zakończenie zostanie ponownie uruchomione. Rozważ to jako funkcję ;-)

(Lub możesz przeskakiwać, ${COMP_LINE:$COMP_POINT-1:1}jeśli lubisz nadmierną inżynierię, ale uważam, że sam bash nie ustawia niezawodnie zmiennych zakończenia podczas tworzenia kopii zapasowej i próby ukończenia w środku polecenia.)

pan. spuratic
źródło
6

Jeśli twój datakatalog zawiera pliki z określonym sufiksem, np. .out Możesz ustawić swoją bashzmienną FIGNOREna ".out"i zostaną one zignorowane. Chociaż można również używać nazw katalogów, nie pomaga to w nazwach bieżących katalogów roboczych.

Przykład:

Twórz tysiące plików testowych 100 KB na fizycznym hoście za pomocą dysków twardych RAID 1:

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

Ustaw bashzmienną:
$ FIGNORE=".json"

Utwórz plik testowy:
$ touch test.out

Przetestuj przy 5000 plików:

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

Nie pojawia się opóźnienie między vi a singlem tabprzedtem test.out.

Przetestuj przy 50 000 plików:

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

Pojedyncza karta tworzy ułamek sekundy opóźnienia przed test.outpojawieniem się.

Przetestuj przy 200 000 plików:

$ 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

Pojawia się opóźnienie 1 sekundy między vi a singlem tabprzed test.out.

Bibliografia:

Wyciągnij rękę od faceta

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
źródło
1
nic nie pomoże, spróbuj utworzyć dir w / około ~ 100k plików. to robię $ vi <tab><tab>, to i tak zajmuje dużo czasu. : \
neizod
nadal nie rozwiązuję problemu, załóżmy, że mam 100 tys. .jsonplików i jeden plik tekstowy o nazwie foo.txt. włączam FIGNORE='.json', piszę $ vi <tab>i wciąż muszę czekać pół minuty, żeby mnie dopełnić $ vi foo.txt. --- prawdziwy scenariusz Nie będę edytować ani mieszać typów plików w tym katalogu, ale jeśli włączę FIGNOREi (przypadkowo) naciśniesz kartę, moja klawiatura będzie zawieszona na długi czas bez cienia podobnego Display all 188275 possibilities? (y or n), co mi mówi że mogę odzyskać klawiaturę.
neizod
@neizod - Przepraszam, próbowałem wielu scenariuszy, aby FIGNORE działał na poziomie katalogu, ale bezskutecznie. Wydaje mi się, że wymagane jest niestandardowe rozwiązanie, albo po prostu wyłączasz ukończenie bash na tym hoście lub wypróbuj rozwiązanie zsh wyszczególnione na naszej siostrzanej stronie, o której wspominał Gilles.
geedoubleya
Niestety, w Twoim systemie jest to tylko 1 sekunda, w moim systemie to około pół minuty. Więc chyba kupiłbym nowy komputer, żeby to rozwiązanie działało.
neizod
0

Zgadnij, czego chcesz (pożyczyłem trochę kodu od @ mr.spuratic)

Pamiętaj, że nie uniemożliwia to naciśnięcia klawisza TAB przy użyciu pełnej ścieżki (np 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
}  
czatować
źródło
Nie zapomnij o pushd/ popd. Ponieważ używa tylko zwykłej tablicy, a nie tablicy asocjacyjnej, będzie działać również w wersji bash 3.x.
mr.spuratic