Disabilitare il completamento della scheda in Bash per alcune directory che contengono un numero elevato di file?


19

Sto lavorando con un gran numero di file, che tengo in una directory. Ogni volta che entro in quella directory e premo accidentalmente Tabdue volte, ci vuole troppo tempo (potrebbe essere più di un minuto) per mostrare i file che corrispondono allo schema, e sono abbastanza infastidito da questo comportamento.

Ad esempio, la mia struttura di directory è:

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

Dal momento che amo ancora il completamento, c'è comunque di disabilitare questa funzione solo nella data/directory? Come impostare il timeout per 5 secondi o uno script che disattiva automaticamente il completamento all'interno di una directory specifica?


Passeresti a zsh su questo? (Non so se sia possibile in bash. So che è possibile in zsh, ma potrebbe non essere facile, non ho controllato.)
Gilles 'SO- smetti di essere malvagio'

Risposte:


9

Questo non è perfetto, ma poi il completamento bash è piuttosto complicato ...

Il modo più semplice è basato su un comando, è leggermente più flessibile di FIGNORE, puoi fare:

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

Ciò indica il completamento automatico del completamento per i vifile e la rimozione dei motivi corrispondenti al -Xfiltro. Il rovescio della medaglia è che il modello non è normalizzato, quindi le ../datavariazioni non corrispondono.

La prossima cosa migliore potrebbe essere una PROMPT_COMMANDfunzione personalizzata :

# 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

Questo disabilita il completamento (completamente) quando ci si trova nella directory, ma lo disabilita per ogni percorso non solo per i file in quella directory.

Sarebbe più generalmente utile disabilitarlo selettivamente per percorsi definiti, ma credo che l'unico modo sia usare una funzione di completamento predefinita (bash-4.1 e successive con complete -D) e molti problemi.

Questo dovrebbe funzionare per te, ma potrebbe avere effetti collaterali indesiderati (ad esempio, modifiche al completamento previsto in alcuni casi):

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

Funziona per il completamento di vi, altri comandi possono essere aggiunti secondo necessità. Dovrebbe interrompere il completamento dei file nelle directory indicate indipendentemente dal percorso o dalla directory di lavoro.

Credo che l'approccio generale complete -Dsia quello di aggiungere dinamicamente funzioni di completamento per ciascun comando quando viene rilevato. Potrebbe anche essere necessario aggiungere complete -E(completamento del nome del comando quando il buffer di input è vuoto).


Aggiornamento Ecco una versione ibrida delle PROMPT_COMMANDsoluzioni con funzioni di completamento, è un po 'più facile da capire e hacking penso:

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

Questa funzione di prompt imposta la nocompletevariabile quando si immette una delle directory configurate. Il comportamento di completamento modificato si attiva solo quando quella variabile non è vuota e solo quando si tenta di completare da una stringa vuota, consentendo così il completamento di nomi parziali (rimuovere la -z "$cur"condizione per impedirne del tutto il completamento). Commenta le due printfrighe per un funzionamento silenzioso.

Altre opzioni includono un .noautocompletefile flag per directory che è possibile touchin una directory secondo necessità; e indovinare la dimensione della directory usando GNU stat. Puoi usare una o tutte queste tre opzioni.

(Il statmetodo è solo una supposizione , la dimensione della directory segnalata cresce con il suo contenuto, è un "segno di riferimento" che di solito non si riduce quando i file vengono eliminati senza alcun intervento amministrativo. È più economico che determinare il contenuto reale di un potenzialmente grande directory. Il comportamento preciso e l'incremento per file dipende dal filesystem sottostante. Lo trovo almeno un indicatore affidabile sui sistemi Linux ext2 / 3/4.)

bash aggiunge uno spazio extra anche quando viene restituito un completamento vuoto (ciò si verifica solo quando si completa alla fine di una riga). È possibile aggiungere -o nospaceal completecomando per impedirlo.

Un altro inconveniente è che se si esegue il backup del cursore all'inizio di un token e si preme la scheda, il completamento predefinito verrà riavviato. Consideralo una caratteristica ;-)

(Oppure potresti andare in giro ${COMP_LINE:$COMP_POINT-1:1}se ti piace un eccesso di ingegneria, ma trovo che bash stesso non riesca a impostare le variabili di completamento in modo affidabile quando esegui il backup e tenti di completarlo nel mezzo di un comando.)


6

Se la tua datadirectory contiene file con un suffisso specifico, ad esempio .out , puoi impostare la tua bashvariabile FIGNOREsu ".out"e questi verranno ignorati. Sebbene sia possibile utilizzare anche nomi di directory, ciò non aiuta sui nomi di directory di lavoro correnti.

Esempio:

Crea migliaia di file di test da 100 KB su host fisico con HDD raid 1:

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

Imposta la bashvariabile:
$ FIGNORE=".json"

Crea il file di prova:
$ touch test.out

Test su 5.000 file:

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

Non viene visualizzato alcun ritardo tra vi e single tabprima test.out.

Test su 50.000 file:

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

La scheda singola crea una frazione di secondo di ritardo prima che test.outappaia.

Test su 200.000 file:

$ 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

Appare un ritardo di 1 secondo tra vi e single tabprima test.out.

Riferimenti:

Estratto dall'uomo 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:~".

1
non aiuta, prova a creare dir con circa ~ 100k file. poi lo faccio $ vi <tab><tab>, ci vuole comunque molto tempo. : \
neizod,

continua a non risolvere il problema, supponiamo che io abbia 100.000 .jsonfile e un singolo file di testo chiamato foo.txt. accendendo FIGNORE='.json', scrivo $ vi <tab>e devo ancora aspettare mezzo minuto, quindi mi completa $ vi foo.txt. --- scenario reale non ho intenzione di modificare o mescolare i tipi di file all'interno di quella directory, ma se accendo FIGNOREe (accidentalmente) premo tab, la mia tastiera si bloccherà a lungo senza accenni a qualcosa del genere Display all 188275 possibilities? (y or n), che mi dice che posso riavere la mia tastiera.
Neizod,

@neizod - Mi dispiace, ho provato molti scenari per far funzionare FIGNORE a livello di directory, senza risultati. Immagino che sia necessaria una soluzione personalizzata, o semplicemente disabiliti il ​​completamento bash su quell'host, o prova la soluzione zsh dettagliata sul nostro sito gemello menzionato da Gilles.
geedoubleya,

Siamo spiacenti, è solo 1 secondo sul tuo sistema, è circa mezzo minuto sul mio sistema. Quindi immagino che andrei a comprare un nuovo PC per far funzionare questa soluzione.
Neizod,

0

Indovina questo è quello che vuoi (preso in prestito un codice da @ mr.spuratic)

Nota che questo non ti impedisce di premere TAB usando il percorso completo (es 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
}  

Non dimenticare di pushd/ popd. Poiché questo utilizza solo un array normale, non un array associativo, funzionerà anche in bash 3.x.
mr.spuratic,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.