禁用Bash中某些包含大量文件的目录的制表符完成功能?


19

我正在处理大量文件,这些文件保存在目录中。每次我进入该目录并无意中按Tab两次时,显示匹配模式的文件花费的时间太长(可能超过一分钟),而我对此行为感到非常恼火。

例如,我的目录结构是:

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

由于我仍然喜欢补全,因此是否只能在data/目录中禁用此功能?例如设置5秒钟的超时时间,还是在特定目录中自动关闭完成功能的脚本?


您会为此切换到zsh吗?(我不知道bash中是否可能。我知道zsh中是否可能,但是可能并不容易,我没有检查过。)
Gilles'SO-别再作恶了

Answers:


9

这不是完美的,但是再次完成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 nospacecomplete命令中以防止这种情况。

剩下的一个小问题是,如果您将光标备份到令牌的开头并单击选项卡,则默认补全将再次出现。认为它是一个功能;-)

(或者,${COMP_LINE:$COMP_POINT-1:1}如果您喜欢过度设计,也可以大惊小怪,但是我发现bash本身在备份并尝试在命令中间完成操作时无法可靠地设置完成变量。)


6

如果您的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:~".

1
将无济于事,请尝试创建包含约100k个文件的目录。那我要做$ vi <tab><tab>,还是要花很多时间。:\
neizod 2014年

仍然无法解决问题,假设我有10万个.json文件,还有一个名为的文本文件foo.txt。打开 FIGNORE='.json',我$ vi <tab>仍然需要等待半分钟,这样我才能完成操作$ vi foo.txt。---真实情况下,我不会编辑或在该目录中混合文件类型,但是如果我打开FIGNORE并(偶然地)按tab,我的键盘将冻结很长时间而没有类似的提示Display all 188275 possibilities? (y or n),告诉我我可以拿回键盘。
neizod 2014年

@neizod-对不起,我尝试了许多方案来使FIGNORE在目录级别工作,但无济于事。我猜需要一个自定义解决方案,或者您仅在该主机上禁用bash完成,或者尝试在我们提到的姊妹网站Gilles上详细介绍的zsh解决方案。
geedoubleya 2014年

抱歉,在您的系统上只有1秒,在我的系统上大约是半分钟。所以我想我会去买一台新的PC来使该解决方案起作用。
neizod 2014年

0

猜猜这就是您想要的(借用@ 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中工作。
spuratic先生2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.