Отключить автодополнение клавишей Tab в Bash для некоторого каталога, содержащего большое количество файлов?

Отключить автодополнение клавишей Tab в Bash для некоторого каталога, содержащего большое количество файлов?

Я работаю с большим количеством файлов, которые я храню в каталоге. Каждый раз, когда я захожу в этот каталог и случайно нажимаю Tabдважды, требуется слишком много времени (может быть, больше минуты), чтобы показать файлы, соответствующие шаблону, и меня это очень раздражает.

Например, моя структура каталогов выглядит так:

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

Поскольку я все еще люблю автодополнение, есть ли возможность отключить эту функцию только в data/каталоге? Например, установить таймаут на 5 секунд или скрипт, который автоматически отключает автодополнение, когда находится внутри определенного каталога?

решение1

Это не идеально, но, с другой стороны, автодополнение bash — довольно сложная штука...

Самый простой способ — на основе каждой команды, он немного более гибкий, чем FIGNORE, вы можете сделать:

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

Это указывает автозаполнению, что завершение для viотносится к файлам, и удалять шаблоны, соответствующие -Xфильтру. Недостатком является то, что шаблон не нормализован, поэтому ../dataи вариации не будут соответствовать.

Следующим лучшим решением может стать пользовательская PROMPT_COMMANDфункция:

# 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

Этототключает завершение(полностью) когда вы находитесь в каталоге,ноон отключает его для каждого пути, а не только для файлов в этом каталоге.

Было бы более полезно выборочно отключать это для определенных путей, но я считаю, что единственный способ — использовать функцию автодополнения по умолчанию (bash-4.1 и более поздние версии с complete -D) и много возни с этим.

Этотдолженработать на вас, но этоможетимеют непреднамеренные побочные эффекты (т.е. изменения ожидаемого завершения в некоторых случаях):

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

Это работает для завершения vi, другие команды могут быть добавлены по мере необходимости. Это должно остановить завершение для файлов в названных каталогах независимо от пути или рабочего каталога.

Я считаю, что общий подход complete -Dзаключается в динамическом добавлении функций завершения для каждой команды по мере ее появления. Также может потребоваться добавить complete -E(завершение имени команды, когда входной буфер пуст).


Обновлять Вот гибридная версия решений PROMPT_COMMANDфункции завершения и , я думаю, ее немного легче понять и взломать:

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

Эта функция приглашения устанавливает nocompleteпеременную, когда вы входите в один из настроенных каталогов. Измененное поведение завершения срабатывает только тогда, когда эта переменная не является пустойитолько когда вы пытаетесь завершить из пустой строки, тем самым позволяя завершить частичные имена (уберите -z "$cur"условие, чтобы полностью предотвратить завершение). Закомментируйте две printfстроки для тихой работы.

Другие варианты включают файл флага для каждого каталога .noautocomplete, который вы можете использовать touchв каталоге по мере необходимости; и угадывание размера каталога с помощью GNU stat. Вы можете использовать любой или все из этих трех вариантов.

( statМетодэто только предположение, сообщаемый размер каталога растет вместе с его содержимым, это «высшая точка», которая обычно не уменьшается при удалении файлов без административного вмешательства. Это дешевле, чем определять реальное содержимое потенциально большого каталога. Точное поведение и приращение на файл зависят от базовой файловой системы. Я считаю это надежным индикатором, по крайней мере, на системах Linux ext2/3/4.)

bash добавляет дополнительный пробел, даже если возвращается пустое завершение (это происходит только при завершении в конце строки). Вы можете добавить -o nospaceк completeкоманде, чтобы предотвратить это.

Осталась одна загвоздка: если вы вернете курсор в начало токена и нажмете Tab, то автодополнение по умолчанию снова включится. Считайте это функцией ;-)

(Или вы можете повозиться с этим, ${COMP_LINE:$COMP_POINT-1:1}если вам нравится излишняя инженерия, но я считаю, что сам bash не может надежно устанавливать переменные завершения, когда вы делаете резервную копию и пытаетесь завершить команду в середине.)

решение2

Если ваш dataкаталог содержит файлы с определенным суффиксом,например .out, то вы можете установить вашу bashпеременную FIGNOREв ".out"и они будут игнорироваться. Хотя можно использовать и имена каталогов, это не поможет для текущих рабочих имен каталогов.

Пример:

Создайте тысячи тестовых файлов размером 100 КБ на физическом хосте с жесткими дисками RAID 1:

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

Установите bashпеременную:
$ FIGNORE=".json"

Создайте тестовый файл:
$ touch test.out

Тест на5000файлы:

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

Никакой задержки между vi и синглом tabперед test.outпоявлением нет.

Тест на50,000файлы:

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

Одна вкладка создает задержку в доли секунды перед test.outпоявлением.

Тест на200,000файлы:

$ 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

Задержка в 1 секунду между vi и синглом tabперед test.outпоявлением.

Использованная литература:

Отрывок из 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:~".

решение3

Думаю, это то, что вам нужно (одолжил код у @mr.spuratic)

Обратите внимание, что это не мешает вам нажать TAB, используя полный путь (например,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
}  

Связанный контент