如何给Bash自动补全选项的某些选项末尾添加空格,而对其他选项不做修改?

18

我正在尝试为一个Java程序创建Bash自动完成脚本。该程序的典型调用可能看起来像这样:

$ javaProgram -Dproperty=foo option1 option2

我的脚本的一部分将是建议此程序可用的各种Java属性(例如,当用户键入-D时,脚本会建议property = ,使用户接着输入值)。

我希望补全后不在等号后插入空格。但是,当用户正在键入程序的各种选项(以上例子中的option1option2)时,我希望脚本完成并在完成时插入一个空格。

我对Bash补全脚本还不熟悉,但我知道complete shell内置的nospace选项。但似乎它对compgen内置无效,而这似乎是我想要的。我尝试使用nospace,然后显式地在适当选项的结尾包含空格,但它们似乎没有通过。

有人知道如何在某些选项末尾获取空格,但在其他选项末尾不获取空格吗?


最简答案 在这里 - Pedram
6个回答

7

您可以通过使用相应的选项删除空格,然后为需要它们的选项手动添加空格,所有这些都可以使用简单的-W(“单词列表”)完成来完成:

complete -o nospace -W 'Dproperty= "option1 " "option2 "' javaProgram

自动补全现在会在option1option2后面插入一个空格,但不会在Dproperty=后面插入空格。


6

C4H5As提到的scp的bash自动补全功能中保留添加空格的特性并不是指“&”符号,这只是sed语法中的“替换匹配项”的表示方式。

你需要改变IFS,以便compgen不会根据空格而是根据换行符或制表符来分割输入:

local IFS=$'\t\n'
COMPREPLY=( $(compgen -W "$options" -- $cur) )

2

您可以使用compopt来实际更改complete选项。这对我有用:

_javaProgram(){
  # The completion will not add a space by default. We will alter
  # this behavior by calling `compopt +o nospace` to neutralize this.

  # In completion, '$2' is the current word, and '$3' is the previous
  case "$3" in
    -D) 
        # No space by default.
        COMPREPLY=( $(compgen -W "property=" -- "$2") )
    ;;
    *)  
        # The `case` default:
        COMPREPLY=( $(compgen -W "your default word list" -- "$2") )
        
        # append a space by calling:
        compopt +o nospace
    ;;
  esac
}

complete -F _javaProgram -o nospace javaProgram

然而,我只需要在短选项后添加空格。为了区分长选项和短选项,您可以检查COMPREPLY建议中是否有=

_javaProgram(){
  # The completion will not add a space by default. We will alter
  # this behavior by calling `compopt +o nospace` to neutralize this.

  # In completion, '$2' is the current word, and '$3' is the previous
  case "$3" in
    -D) 
        # Do not worry about appending spaces here ... .
        COMPREPLY=( $(compgen -W "property=" -- "$2") )
    ;;
    *)  
        # Do not worry about appending spaces here ... .
        COMPREPLY=( $(compgen -W "your default word list" -- "$2") )
    ;;
  esac

if [[ ! "${COMPREPLY[@]}" =~ "=" ]]; then
  # Add space if there is not a '=' in suggestions
  compopt +o nospace
fi
}

complete -F _javaProgram -o nospace javaProgram

1

丹尼斯说得很对,在你引用带有complete -F _your_func的函数中,你想要像这样的东西(来自/etc/bash_completion):

    # escape spaces; remove executables, aliases, pipes and sockets;
    # add space at end of file names
    COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
               command ls -aF1d "$path*" 2>/dev/null | \
               sed -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\\\\\\\\\&/g" \
               -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
    return 0

这个例子使用了-o nospace选项,但手动添加了空格。最后一个-e ('s/[^\/]$/& /g')是添加空格的。在这种情况下,仅当最后一个参数不以斜杠结尾(这将是一个目录,并具有自动完成的文件)时才添加。

关键的区别似乎是在空格之前添加了一个“&”。尝试一下,看看是否有效。


1

看到你目前的代码将会很有帮助。

不过,如果你有的话,可以看一下/etc/bash_completion.d/sshscp 的完成代码。它在本地文件名后面放置空格,但在主机名后面不放置。


1
正如海报所提到的,无法将-o nospace选项传递给compgen。一种可能性是定义两个函数,其中一个在调用compgen之前插入对compopt -o nospace的调用(另一个函数则不插入)。
例如:
__my_complete_no_space() {
    local IFS=$' \t\n' cur_="${3-$cur}"
    [[ $cur_ == *:* ]] && cur_="${cur#*:}"
    compopt -o nospace
    COMPREPLY=( $(compgen -P "${2-}" -W "$1" -S "${4-}" -- "$cur_") )
}

这在需要根据上下文来确定是否添加空格时非常有用。

1
可以使用compopt将选项传递给complete。请查看此答案 - Pedram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接