16

以下 bash 补全将一组可能的单词(即补全)传递给 compgen。

basenames=("foo" "fu bar" "baz");

COMPREPLY=($(compgen -W "${basenames[*]}" -- "${COMP_WORDS[COMP_CWORD]}"))

问题是数组元素中的空格没有被保留,也就是说,由于分词,“foo bar”被视为元素。有没有办法保留空白,以便显示 3 个元素,而不是 4 个?

编辑

basenames包含文件名,即几乎每个字符(除了 / 和 \0)都是允许的。

编辑 2

-W 标志需要一个单词,例如foo bar foobar. 将多个元素传递给它(这是${basenames[@]}会做的)将不起作用。

编辑 3

更改了示例 basenames 数组(因此 thefoofoofromfoo bar不会被折叠)。

使用换行符分隔单词有效:

local IFS=$'\n'
COMPREPLY=($(compgen -W "$(printf "%s\n" "${basenames[@]}")" --  ${COMP_WORDS[COMP_CWORD]}"))

使用\0不会:

local IFS=$'\0'
COMPREPLY=($(compgen -W "$(printf "%s\0" "${basenames[@]}")" --  ${COMP_WORDS[COMP_CWORD]}"))
4

4 回答 4

11

为什么要打扰compgen?只需手动将它们添加到 COMPREPLY。以下将完成匹配的文件名/some/path,安全地处理文件名。

some_completion_function() {
    local files=("/some/path/$2"*)
    [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]##*/}" )
}

不可能compgen安全地处理文件名。

于 2012-07-14T09:26:27.553 回答
1

也许这可能会:

COMPREPLY=($(compgen -W "$(printf "%q " "${basenames[*]}")" -- "${COMP_WORDS[COMP_CWORD]}"))
于 2012-05-18T18:47:28.583 回答
0

在那里阅读更好的解决方案

使用readarrayprintf

readarray -t words < /etc/passwd
compgen -W  "$(printf "'%s' " "${words[@]}")" -- "man"
# OUTPUTS: man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
  • readarray:避免处理 IFS
  • printf: Escape: 或者@Qbash 4.4后的参数转换(20170man bash搜索 parameter@operator
于 2020-09-06T03:16:04.523 回答
-3

您几乎应该总是使用 at 符号而不是星号来为数组下标。

COMPREPLY=($(compgen -W "${basenames[@]}" -- "${COMP_WORDS[COMP_CWORD]}"))

来自man bash(或参见Bash 参考手册):

如果下标是@*,则单词扩展为 name 的所有成员。只有当单词出现在双引号中时,这些下标才会有所不同。如果单词是双引号,则${name[*]}扩展为单个单词,其中每个数组成员的值由 IFS 特殊变量的第一个字符分隔,并将${name[@]}name 的每个元素扩展为单独的单词。

换句话说,at 符号形式“扁平化”了数组。星号形式保留它。

于 2012-05-18T17:32:18.677 回答