Вимкнути заповнення вкладки в Bash для деякого каталогу, який містить велику кількість файлів?


19

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

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

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

Оскільки я все ще люблю завершення, чи все-таки можна відключити цю функцію лише в data/каталозі? Як встановити тайм-аут на 5 секунд, або сценарій, який автоматично вимикає завершення, коли знаходиться всередині певного каталогу?


Ви б перейшли до zsh через це? (Я не знаю, чи можливо це в баш. Я знаю, що це можливо в zsh, але це може бути непросто, я не перевіряв.)
Жил 'SO- перестань бути злим'

Відповіді:


9

Це не ідеально, але знову ж таки завершення баш - досить складна справа ...

Найпростіший спосіб - це команда, трохи гнучкіша, ніж 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 Linux ext2 / 3/4.)

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

Одне, що залишилось, - якщо створити резервну копію курсору до початку вкладки маркера та натискання, завершення за замовчуванням розпочнеться знову. Вважайте це особливістю ;-)

(Або ви можете пограбувати, ${COMP_LINE:$COMP_POINT-1:1}якщо вам подобається надмірна інженерія, але я вважаю, що сам Bash не в змозі надійно встановити змінні завершення, коли ви створюєте резервну копію та намагаєтеся виконати в середині команди.)


6

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

Приклад:

Створіть тисячі тестових файлів на 100 КБ на фізичному хості з набіжними 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:~".

1
не допоможе, спробуйте створити dir w / about ~ 100k файлів. то я $ vi <tab><tab>, це все одно займає багато часу. : \
neizod

все ще не вирішити проблему, припустимо, у мене є .jsonфайли 100k та один текстовий файл з ім’ям foo.txt. увімкнувши FIGNORE='.json', я набираю, $ vi <tab>і мені ще потрібно почекати півхвилини, щоб воно завершило мене $ vi foo.txt. --- реальний сценарій Я не збираюсь редагувати або змішувати типи файлів усередині цього режиму, але якщо я ввімкну FIGNOREі (випадково) натисніть вкладку, моя клавіатура надовго заморозиться, не натякаючи на щось подібне Display all 188275 possibilities? (y or n), що мені кажуть що я можу мати назад свою клавіатуру
neizod

@neizod - Вибачте, я спробував багато сценаріїв, щоб змусити FIGNORE працювати на рівні каталогу, безрезультатно. Я думаю, що потрібне спеціальне рішення, або ви просто відключите завершення bash на цьому хості, або спробуйте рішення zsh, детальне на нашому сестринському сайті Gilles.
geedoubleya

Вибачте, це лише 1 секунда у вашій системі, це близько півхвилини в моїй системі. Тож я думаю, я б пішов купити новий ПК, щоб це рішення працювало.
neizod

0

Здогадайтесь, це те, що ви хочете (запозичив якийсь код у @ 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
}  

Не забувайте про pushd/ popd. Оскільки для цього використовується лише звичайний масив, а не асоціативний масив, він також буде працювати в bash 3.x.
mr.spuratic
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.