在 Bash 中为某些包含大量文件的目录禁用选项卡完成?

nei*_*zod 19 bash autocomplete

我正在处理大量文件,这些文件保存在一个目录中。每次我进入该目录Tab并不小心按两次时,显示与模式匹配的文件需要很长时间(可能超过一分钟),而且我对这种行为感到非常恼火。

比如我的目录结构是:

my-project/
??? docs/
??? data/        <---- contains around 200k files.
??? analyser/
Run Code Online (Sandbox Code Playgroud)

由于我仍然喜欢完成,无论如何只能在data/目录中禁用此功能吗?像设置超时 5 秒,或者在特定目录中自动关闭完成的脚本?

mr.*_*tic 10

这并不完美,但是再次 bash 完成是一件非常棘手的事情......

最简单的方法是基于每个命令,它比 稍微灵活FIGNORE,您可以这样做:

 complete -f -X "/myproject/data/*" vi
Run Code Online (Sandbox Code Playgroud)

这指示自动完成为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
Run Code Online (Sandbox Code Playgroud)

当您在目录中时,这会禁用完成(完全),但它会为每个路径禁用它,而不仅仅是该目录中的文件。

对定义的路径有选择地禁用它会更普遍有用,但我相信唯一的方法是使用默认的完成功能(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
Run Code Online (Sandbox Code Playgroud)

这适用于完成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
Run Code Online (Sandbox Code Playgroud)

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 本身无法可靠地设置完成变量。)


gee*_*eya 6

如果您的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
Run Code Online (Sandbox Code Playgroud)

设置bash变量:
$ FIGNORE=".json"

创建测试文件:
$ touch test.out

测试5,000 个文件:

$ ls *.json|wc -l
5587
$ vi test.out
Run Code Online (Sandbox Code Playgroud)

vi 和single tabbefore之间没有延迟test.out出现。

测试50,000 个文件:

$ ls *.json|wc -l
51854
$ vi test.out
Run Code Online (Sandbox Code Playgroud)

单个选项卡在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
Run Code Online (Sandbox Code Playgroud)

在vi之间1秒延迟和单tab之前test.out出现。

参考:

来自 man bash 的 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:~".
Run Code Online (Sandbox Code Playgroud)