在 Bash 中對某些包含大量檔案的目錄禁用製表符補全?

在 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",這些將被忽略。儘管也可以使用目錄名稱,但這對目前工作目錄名稱沒有幫助。

例子:

在具有 raid 1 HDD 的實體主機上建立數千個 100KB 測試檔案:

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

測試於5,000文件:

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

vi 和 single tabbefore之間不會test.out出現任何延遲。

測試於50,000文件:

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

單一選項卡在出現之前會產生幾分之一秒的延遲test.out

測試於20萬文件:

$ 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 和 single tabbefore之間test.out出現 1 秒的延遲。

參考:

摘自 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
}  

相關內容