Nonaktifkan penyelesaian tab di Bash untuk beberapa direktori yang berisi banyak file?

19

Saya bekerja dengan sejumlah besar file, yang saya simpan di direktori. Setiap kali saya masuk ke direktori itu dan secara tidak sengaja menekan Tabdua kali, butuh waktu terlalu lama (mungkin lebih dari satu menit) untuk menampilkan file yang cocok dengan pola tersebut, dan saya cukup terganggu dengan perilaku ini.

Sebagai contoh, struktur direktori saya adalah:

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

Karena saya masih suka penyelesaian, apakah ada untuk menonaktifkan fitur ini hanya pada data/direktori? Suka mengatur batas waktu selama 5 detik, atau skrip yang secara otomatis mematikan penyelesaian ketika berada di dalam direktori tertentu?

Neizod
sumber
Apakah Anda akan beralih ke zsh karena ini? (Aku tidak tahu apakah itu mungkin di bash. Aku tahu itu mungkin di zsh, tapi itu mungkin tidak mudah, aku belum memeriksanya.)
Gilles 'SO- stop being evil'

Jawaban:

9

Ini tidak sempurna, tetapi sekali lagi penyelesaian pesta adalah hal yang cukup sulit ...

Cara paling sederhana adalah berdasarkan per-perintah, ini sedikit lebih fleksibel daripada FIGNOREyang bisa Anda lakukan:

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

Ini menginstruksikan autocomplete bahwa penyelesaian viuntuk file, dan untuk menghapus pola yang cocok dengan -Xfilter. Kelemahannya adalah bahwa pola tersebut tidak dinormalisasi, jadi ../datadan variasi tidak akan cocok.

Hal terbaik berikutnya mungkin adalah PROMPT_COMMANDfungsi khusus :

# 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

Ini menonaktifkan penyelesaian (sepenuhnya) ketika Anda berada di direktori, tetapi menonaktifkannya untuk setiap jalur bukan hanya file di direktori itu.

Akan lebih bermanfaat secara umum untuk menonaktifkan selektif ini untuk jalur yang ditentukan, tapi saya percaya satu-satunya cara adalah menggunakan fungsi penyelesaian standar (bash-4.1 dan yang lebih baru dengan complete -D) dan banyak mengacaukannya.

Ini seharusnya bekerja untuk Anda, tetapi mungkin memiliki efek samping yang tidak diinginkan (yaitu perubahan pada penyelesaian yang diharapkan dalam beberapa kasus):

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

Ini berfungsi untuk penyelesaian vi, perintah lain dapat ditambahkan sesuai kebutuhan. Seharusnya berhenti menyelesaikan untuk file dalam direktori bernama terlepas dari path atau direktori kerja.

Saya percaya pendekatan umum dengan complete -Dadalah secara dinamis menambahkan fungsi penyelesaian untuk setiap perintah saat ditemui. Seseorang mungkin juga perlu menambahkan complete -E(penyelesaian nama perintah ketika buffer input kosong).


Pembaruan Berikut adalah versi hybrid dari PROMPT_COMMANDsolusi fungsi penyelesaian dan, saya pikir ini lebih mudah dipahami dan diretas:

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

Fungsi prompt ini mengatur nocompletevariabel ketika Anda memasukkan salah satu direktori yang dikonfigurasi. Perilaku penyelesaian yang dimodifikasi hanya menendang ketika variabel itu tidak kosong dan hanya ketika Anda mencoba menyelesaikan dari string kosong, sehingga memungkinkan penyelesaian nama parsial (menghapus -z "$cur"kondisi untuk mencegah penyelesaian sama sekali). Komentari dua printfbaris untuk operasi senyap.

Pilihan lain termasuk .noautocompletefile flag per-direktori yang Anda bisa touchdi direktori sesuai kebutuhan; dan menebak ukuran direktori menggunakan GNU stat. Anda dapat menggunakan salah satu atau ketiga opsi tersebut.

( statMetode ini hanya tebakan , ukuran direktori yang dilaporkan tumbuh bersama isinya, ini adalah "tanda air tinggi" yang biasanya tidak akan menyusut ketika file dihapus tanpa intervensi administratif. Lebih murah daripada menentukan konten nyata dari potensi besar direktori .Tingkah laku yang tepat dan peningkatan per file tergantung pada sistem file yang mendasarinya. Saya menemukan itu indikator yang dapat diandalkan pada sistem linux ext2 / 3/4 setidaknya.)

bash menambahkan ruang ekstra bahkan ketika penyelesaian kosong dikembalikan (ini hanya terjadi ketika menyelesaikan di akhir baris). Anda dapat menambahkan -o nospaceke completeperintah untuk mencegah hal ini.

Satu niggle yang tersisa adalah bahwa jika Anda mencadangkan kursor ke awal tab token dan tekan, penyelesaian default akan menendang lagi. Anggap itu fitur ;-)

(Atau Anda bisa lanjut dengan ${COMP_LINE:$COMP_POINT-1:1}jika Anda suka over-engineering, tapi saya menemukan bash sendiri gagal untuk mengatur variabel penyelesaian andal ketika Anda mencadangkan dan mencoba menyelesaikan di tengah perintah.)

mr.spuratic
sumber
6

Jika Anda datadirektori berisi file dengan akhiran tertentu, misalnya .out , maka Anda dapat mengatur Anda bashvariabel FIGNOREuntuk ".out"dan ini akan diabaikan. Meskipun dimungkinkan untuk menggunakan nama direktori juga, ini tidak membantu pada nama direktori kerja saat ini.

Contoh:

Buat ribuan file uji 100KB pada host fisik dengan serangan 1 HDD:

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

Setel bashvariabel:
$ FIGNORE=".json"

Buat file uji:
$ touch test.out

Tes pada 5.000 file:

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

Tidak ada penundaan antara vi dan tunggal tabsebelum test.outmuncul.

Tes pada 50.000 file:

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

Tab tunggal menciptakan sebagian kecil dari penundaan kedua sebelum test.outmuncul.

Tes pada 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

Keterlambatan 1 detik antara vi dan tunggal tabsebelum test.outmuncul.

Referensi:

Pakar dari bash pria

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
sumber
1
tidak akan membantu, coba buat dir w / sekitar ~ 100k file. maka saya lakukan $ vi <tab><tab>, itu butuh banyak waktu. : \
neizod
masih belum menyelesaikan masalah, misalkan saya memiliki 100 ribu .jsonfile, dan satu file teks bernama foo.txt. Menghidupkan FIGNORE='.json', saya mengetik $ vi <tab>dan masih perlu menunggu setengah menit sehingga itu menyelesaikan saya $ vi foo.txt. --- skenario nyata saya tidak akan mengedit, atau mencampur jenis file di dalam dir itu, tetapi jika saya mengaktifkan FIGNOREdan (secara tidak sengaja) tekan tab, keyboard saya akan membeku untuk waktu yang lama tanpa sedikit pun seperti Display all 188275 possibilities? (y or n), yang memberitahu saya bahwa saya dapat memiliki keyboard saya kembali.
neizod
@neizod - Maaf, saya mencoba banyak skenario untuk membuat FIGNORE berfungsi pada level direktori, tetapi tidak berhasil. Saya kira solusi khusus diperlukan, atau Anda cukup menonaktifkan penyelesaian bash pada host itu, atau coba solusi zsh yang dirinci pada situs saudara kami yang disebutkan Gilles.
geedoubleya
Maaf, ini hanya 1 detik pada sistem Anda, sekitar setengah menit pada sistem saya. Jadi saya kira saya akan membeli PC baru untuk membuat solusi ini berfungsi.
neizod
0

Kira ini yang Anda inginkan (meminjam beberapa kode dari @ mr.spuratic)

Catatan ini tidak mencegah Anda menekan TAB menggunakan jalur lengkap (mis 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
sumber
Jangan lupa tentang pushd/ popd. Karena ini hanya menggunakan array biasa, bukan array asosiatif itu akan bekerja di bash 3.x juga.
mr.spuratic