Я работаю с большим количеством файлов, которые я храню в каталоге. Каждый раз, когда я захожу в этот каталог и случайно нажимаю 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
}