我正在处理大量文件,这些文件保存在目录中。每次我进入该目录并无意中按Tab两次时,显示匹配模式的文件花费的时间太长(可能超过一分钟),而我对此行为感到非常恼火。
例如,我的目录结构是:
my-project/
├── docs/
├── data/ <---- contains around 200k files.
└── analyser/
由于我仍然喜欢补全,因此是否只能在data/目录中禁用此功能?例如设置5秒钟的超时时间,还是在特定目录中自动关闭完成功能的脚本?
我正在处理大量文件,这些文件保存在目录中。每次我进入该目录并无意中按Tab两次时,显示匹配模式的文件花费的时间太长(可能超过一分钟),而我对此行为感到非常恼火。
例如,我的目录结构是:
my-project/
├── docs/
├── data/ <---- contains around 200k files.
└── analyser/
由于我仍然喜欢补全,因此是否只能在data/目录中禁用此功能?例如设置5秒钟的超时时间,还是在特定目录中自动关闭完成功能的脚本?
Answers:
这不是完美的,但是再次完成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命令中以防止这种情况。
剩下的一个小问题是,如果您将光标备份到令牌的开头并单击选项卡,则默认补全将再次出现。认为它是一个功能;-)
(或者,${COMP_LINE:$COMP_POINT-1:1}如果您喜欢过度设计,也可以大惊小怪,但是我发现bash本身在备份并尝试在命令中间完成操作时无法可靠地设置完成变量。)
如果您的data目录包含带有特定后缀的文件,例如 .out,则可以将bash变量设置FIGNORE为".out",这些将被忽略。尽管也可以使用目录名称,但这对当前工作目录名称没有帮助。
例:
使用RAID 1硬盘在物理主机上创建数千个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和单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
vi和单tab前之间延迟1秒test.out出现。
参考文献:
来自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:~".
$ vi <tab><tab>,还是要花很多时间。:\
.json文件,还有一个名为的文本文件foo.txt。打开 FIGNORE='.json',我$ vi <tab>仍然需要等待半分钟,这样我才能完成操作$ vi foo.txt。---真实情况下,我不会编辑或在该目录中混合文件类型,但是如果我打开FIGNORE并(偶然地)按tab,我的键盘将冻结很长时间而没有类似的提示Display all 188275 possibilities? (y or n),告诉我我可以拿回键盘。
猜猜这就是您想要的(借用@ 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中工作。