Desativar a conclusão da guia no Bash para algum diretório que contenha um grande número de arquivos?

19

Estou trabalhando com um grande número de arquivos, que guardo em um diretório. Toda vez que vou para esse diretório e pressiono acidentalmente Tabduas vezes, leva muito tempo (pode demorar mais de um minuto) para mostrar os arquivos que correspondem ao padrão, e fico bastante irritado com esse comportamento.

Por exemplo, minha estrutura de diretórios é:

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

Como ainda amo a conclusão, existe alguma maneira de desativar esse recurso apenas no data/diretório? Como definir o tempo limite por 5 segundos ou um script que desativa automaticamente a conclusão quando dentro de um diretório específico?

neizod
fonte
Você mudaria para o zsh por causa disso? (Não sei se é possível no bash. Sei que é possível no zsh, mas pode não ser fácil, não verifiquei.)
Gilles 'SO- stop be evil'

Respostas:

9

Isso não é perfeito, mas, novamente, a conclusão do bash é uma coisa bastante complicada ...

A maneira mais simples é por comando, é um pouco mais flexível do que FIGNOREvocê pode:

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

Isso instrui o preenchimento automático de que a conclusão vié para arquivos e para remover os padrões correspondentes ao -Xfiltro. A desvantagem é que o padrão não é normalizado e, portanto, as ../datavariações não coincidem.

A próxima melhor coisa pode ser uma PROMPT_COMMANDfunção personalizada :

# 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

Isso desabilita a conclusão (completamente) quando você está no diretório, mas desabilita-o para todos os caminhos, não apenas para os arquivos desse diretório.

Em geral, seria mais útil desabilitar isso seletivamente para caminhos definidos, mas acredito que a única maneira é usar uma função de conclusão padrão (bash-4.1 e posterior com complete -D) e muita confusão.

Isso deve funcionar para você, mas pode ter efeitos colaterais indesejados (por exemplo, alterações na conclusão esperada em alguns casos):

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

Isso funciona para a conclusão de vi, outros comandos podem ser adicionados conforme necessário. Ele deve parar a conclusão de arquivos nos diretórios nomeados, independentemente do caminho ou diretório de trabalho.

Acredito que a abordagem geral com complete -Dé adicionar dinamicamente funções de conclusão para cada comando conforme ele é encontrado. Também pode ser necessário adicionar complete -E(conclusão do nome do comando quando o buffer de entrada estiver vazio).


Atualização Aqui está uma versão híbrida das PROMPT_COMMANDsoluções da função e conclusão, é um pouco mais fácil de entender e hackear, eu acho:

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

Essa função de prompt define a nocompletevariável quando você digita um dos diretórios configurados. O comportamento de conclusão modificado entra em ação apenas quando essa variável está em branco e somente quando você tenta concluir a partir de uma sequência vazia, permitindo a conclusão de nomes parciais (remova a -z "$cur"condição para impedir a conclusão completa). Comente as duas printflinhas para operação silenciosa.

Outras opções incluem um .noautocompletearquivo de sinalizador por diretório que você pode touchem um diretório conforme necessário; e adivinhando o tamanho do diretório usando o GNU stat. Você pode usar qualquer uma ou todas essas três opções.

(O statmétodo é apenas um palpite , o tamanho do diretório relatado aumenta com o conteúdo, é uma "marca d'água alta" que geralmente não diminui quando os arquivos são excluídos sem alguma intervenção administrativa. É mais barato do que determinar o conteúdo real de um site potencialmente grande Comportamento preciso e incremento por arquivo dependem do sistema de arquivos subjacente. Acho que é um indicador confiável nos sistemas Linux ext2 / 3/4, pelo menos.)

O bash adiciona um espaço extra, mesmo quando uma conclusão vazia é retornada (isso ocorre apenas quando a conclusão é finalizada no final de uma linha). Você pode adicionar -o nospaceao completecomando para evitar isso.

Um niggle restante é que, se você fizer backup do cursor no início de um token e clicar na guia, a conclusão padrão será ativada novamente. Considere isso um recurso ;-)

(Ou você pode brincar com isso ${COMP_LINE:$COMP_POINT-1:1}se quiser engenharia em excesso, mas acho que o bash falha ao definir as variáveis ​​de conclusão de maneira confiável quando você faz backup e tenta concluir no meio de um comando.)

mr.spuratic
fonte
6

Se o seu datadiretório contiver arquivos com um sufixo específico, por exemplo .out , você poderá definir sua bashvariável FIGNOREcomo ".out"e estes serão ignorados. Embora seja possível usar nomes de diretório também, isso não ajuda nos nomes de diretório de trabalho atuais.

Exemplo:

Crie milhares de arquivos de teste de 100 KB no host físico com HDDs de ataque 1:

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

Defina a bashvariável:
$ FIGNORE=".json"

Crie o arquivo de teste:
$ touch test.out

Teste em 5.000 arquivos:

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

Nenhum atraso entre o vi e o single tabanterior test.outaparece.

Teste em 50.000 arquivos:

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

A guia Única cria uma fração de segundo de atraso antes que test.outapareça.

Teste em 200.000 arquivos:

$ 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

Um atraso de 1 segundo entre o vi e o single tabanterior é test.outexibido.

Referências:

Excert de homem 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
fonte
1
não vai ajudar, tente criar dir com aproximadamente ~ 100k arquivos. então eu faço $ vi <tab><tab>, leva muito tempo de qualquer maneira. : \
neizod
ainda não resolveu o problema, suponha que eu tenha 100 mil .jsonarquivos e um único arquivo de texto chamado foo.txt. ligando FIGNORE='.json', digito $ vi <tab>e ainda preciso esperar meio minuto para que eu me complete $ vi foo.txt. --- cenário real Eu não vou editar ou misturar tipos de arquivos dentro desse diretório, mas se eu ligar FIGNOREe (acidentalmente) pressionar tab, meu teclado ficará paralisado por um longo tempo sem a menor sugestão de algo como Display all 188275 possibilities? (y or n), o que me diz que posso ter meu teclado de volta.
neizod
@neizod - Desculpe, tentei vários cenários para fazer com que o FIGNORE funcionasse no nível de diretório, sem sucesso. Eu acho que é necessária uma solução personalizada, ou você simplesmente desativa a conclusão do bash nesse host ou tenta a solução zsh detalhada no site irmão mencionado por Gilles.
geedoubleya
Desculpe, é apenas 1 segundo no seu sistema, é cerca de meio minuto no meu sistema. Então, eu acho que compraria um PC novo para fazer essa solução funcionar.
Neizod
0

Acho que é isso que você deseja (emprestou algum código de @ mr.spuratic)

Observe que isso não impede que você pressione TAB usando o caminho completo (por exemplo, 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
fonte
Não esqueça pushd/ popd. Como isso usa apenas uma matriz regular, não uma matriz associativa, também funcionará no bash 3.x.
mr.spuratic