我正在處理大量文件,並將它們保存在目錄中。每次我進入該目錄並意外按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 tab
before之間不會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 tab
before之間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
}