Çok sayıda dosya içeren bazı dizinler için Bash'te sekme tamamlama devre dışı bırakılsın mı?


19

Bir dizinde sakladığım çok sayıda dosyayla çalışıyorum. Bu dizine her girdiğimde ve yanlışlıkla Tabiki kez bastığımda, kalıpla eşleşen dosyaları göstermek çok uzun (bir dakikadan fazla sürebilir) sürer ve bu davranıştan oldukça rahatsızım.

Örneğin, dizin yapım:

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

Tamamlamayı hala sevdiğim için, bu özelliği yalnızca data/dizinde devre dışı bırakmanın bir yolu var mı? Ayarlanan zaman aşımı süresi 5 saniye mi yoksa belirli bir dizinin içindeyken tamamlanmayı otomatik olarak kapatan bir komut dosyası gibi mi?


Bunun üzerinde zsh'ye geçer misiniz? (Bash'da mümkün olup olmadığını bilmiyorum. Zsh'de mümkün olduğunu biliyorum, ama kolay olmayabilir, kontrol etmedim.)
Gilles 'SO- kötü olmayı kes'

Yanıtlar:


9

Bu mükemmel değil, ama sonra tekrar bash tamamlanması oldukça zor bir şey ...

En basit yol, komut başına esasında, FIGNOREyapabileceğinizden biraz daha esnektir :

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

Bu, tamamlamanın vidosyalar için olduğunu ve -Xfiltreyle eşleşen kalıpları kaldırmasını otomatik olarak tamamlar . Dezavantajı, desenin normalleştirilmemesi ../datave varyasyonların eşleşmemesidir.

Bir sonraki en iyi şey özel bir PROMPT_COMMANDişlev olabilir :

# 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

Bu , dizinde olduğunuzda tamamlamayı (tamamen) devre dışı bırakır , ancak yalnızca bu dizindeki dosyalar için değil, her yol için devre dışı bırakır.

Tanımlı yollar için bunu seçici olarak devre dışı bırakmak daha genel olarak yararlı olacaktır, ancak tek yolun varsayılan tamamlama işlevini (bash-4.1 ve üstü ile complete -D) ve çok fazla karışıklık kullanmak olduğuna inanıyorum .

Bu gerektiğini sizin için çalışacak, ama bu olabilir istenmeyen yan etkilere (yani bazı durumlarda beklenen tamamlanma değiştirir) vardır:

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

Bu viişlemin tamamlanması için çalışır , gerektiğinde diğer komutlar eklenebilir. Yoldan veya çalışma dizininden bağımsız olarak, adlandırılmış dizinlerdeki dosyaların tamamlanmasını durdurmalıdır.

Genel yaklaşım ile complete -Dkarşılaşılan her komut için dinamik olarak tamamlama işlevleri eklemek olduğuna inanıyorum . Birinin de eklenmesi gerekebilir complete -E(giriş arabelleği boş olduğunda komut adının tamamlanması).


Güncelleme İşte PROMPT_COMMANDve tamamlama fonksiyonu çözümlerinin hibrit bir versiyonu, bence anlamak ve kesmek biraz daha kolay:

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

Bu komut istemi işlevi nocomplete, yapılandırılmış dizinlerden birini girdiğinizde değişkeni ayarlar . Değiştirilen tamamlama davranışı yalnızca bu değişken boş olmadığında ve yalnızca boş bir dizeden tamamlamaya çalıştığınızda devreye girer ve böylece kısmi adların tamamlanmasına izin verir ( -z "$cur"tamamlamayı tamamen önlemek için koşulu kaldırın ). printfSessiz çalışma için iki satırı yorumlayın.

Diğer seçenekler arasında , bir dizinde gerektiğinde .noautocompleteyapabileceğiniz dizin başına bayrak dosyası touch; ve GNU kullanarak dizin boyutunun tahmin edilmesi stat. Bu üç seçenekten herhangi birini veya tümünü kullanabilirsiniz.

( statYöntem sadece bir tahmindir , rapor edilen dizin boyutu içeriği ile birlikte büyür, bazı idari müdahaleler olmadan dosyalar silindiğinde genellikle küçülmeyen bir "yüksek su işareti" dir. Potansiyel olarak büyük bir dosyanın gerçek içeriğini belirlemek daha ucuz Dosya başına kesin davranış ve artış, altta yatan dosya sistemine bağlıdır. En azından linux ext2 / 3/4 sistemlerinde güvenilir bir gösterge buluyorum.)

bash, boş bir tamamlama döndürülse bile fazladan bir boşluk ekler (bu yalnızca bir satırın sonunda tamamlandığında oluşur). Bunu önlemek -o nospaceiçin completekomuta ekleyebilirsiniz .

Kalan bir ince eleyip, imleci bir token ve hit sekmesinin başlangıcına yedeklerseniz, varsayılan tamamlamanın tekrar başlayacağı yönündedir. Bir özellik olarak düşünün ;-)

(Veya ${COMP_LINE:$COMP_POINT-1:1}aşırı mühendislikten hoşlanıyorsanız etrafta dolaşabilirsiniz , ancak yedeklediğinizde ve bir komutun ortasında tamamlamayı denediğinizde bash'ın kendisinin tamamlama değişkenlerini güvenilir bir şekilde ayarlayamadığını görüyorum.)


6

Senin Eğer datadizin belirli sonek ile dosyaları içeren örneğin .out , o zaman ayarlayabilirsiniz bashdeğişkeni FIGNOREiçin ".out"ve bu göz ardı edilecektir. Dizin adlarını da kullanmak mümkün olsa da, bu durum geçerli çalışan dizin adlarında yardımcı olmaz.

Misal:

Raid 1 HDD'lerle fiziksel ana bilgisayarda binlerce 100KB test dosyası oluşturun:

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

bashDeğişkeni ayarlayın :
$ FIGNORE=".json"

Test dosyasını oluşturun:
$ touch test.out

5.000 dosyada test edin :

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

Vi ve single arasında daha tabönce hiçbir gecikme test.outgörünmez.

50.000 dosyada test edin :

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

Tek sekme test.outgörünmeden önce ikinci bir gecikmenin bir kısmını oluşturur .

200.000 dosyada test edin :

$ 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

Vi arasında 1 saniyeden geciktirilmesi ve tek tabönce test.outgörünür.

Referanslar:

Adam bash gelen excert

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
yardımcı olmaz, dir ~ yaklaşık 100k dosyaları oluşturmak deneyin. o zaman yaparım $ vi <tab><tab>, zaten çok zaman alıyor. : \
neizod

yine de sorunu çözmüyorum, 100k dosyam olduğunu .jsonve tek bir metin dosyasının olduğunu varsayalım foo.txt. açıyor FIGNORE='.json', yazıyorum $ vi <tab>ve hala yarım dakika beklemeliyim ki beni tamamlayacak $ vi foo.txt. --- gerçek senaryo Ben bu dir içinde dosya türlerini düzenlemek veya karıştırmak olmayacak, ama FIGNOREsekme açar ve (yanlışlıkla) basarsanız, klavyem gibi bir şey ipucu olmadan uzun bir süre donacaktır Display all 188275 possibilities? (y or n), ki bu bana söyle klavyemi geri alabilirim.
neizod

Maalesef, FIGNORE'un dizin düzeyinde çalışmasını sağlamak için birçok senaryo denedim. Sanırım özel bir çözüm gerekiyor ya da o ana bilgisayarda bash tamamlamayı devre dışı bırakıyorsunuz ya da Gilles'in bahsettiği kardeş sitemizde ayrıntılı olarak açıklanan zsh çözümünü deneyin.
geedoubleya

Üzgünüz, sisteminizde sadece 1 saniye, sistemimde yaklaşık yarım dakika var. Bu yüzden, bu çözümün çalışması için yeni bir bilgisayar almaya giderdim.
neizod

0

Sanırım bu istediğini (@ mr.spuratic bazı kod ödünç)

Bunun, tam yolu kullanarak SEKME tuşuna basmanızı engellemediğini unutmayın (ör. 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
}  

Unutmayın pushd/ ' popd. Bu, ilişkilendirilebilir bir dizi değil, yalnızca normal bir dizi kullandığından, bash 3.x'de de çalışır.
mr.spuratic
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.