为Bash完成设置逐个命令的不区分大小写选项。

8

有没有一种方法可以指定特定的命令不区分大小写,而不是全局开启不区分大小写(至少对于该shell)?

在我的特定情况下,我有一个小应用程序,它使我可以通过命令行访问电子邮件地址数据库,因此我键入:

db get email john smith

它会返回约翰·史密斯的电子邮件地址。因此,我已经成功地在应用程序内部启用了自动完成功能:设置

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

我希望能通过按Tab键来自动补全“get”和“email”。但是,如果我输入“j<tab>”,它就无法完成自动补全,因为在电子邮件数据库中,它被正确地大写。我想让bash无论如何都能够完成自动补全。(如果我使用大写的“J”,它可以正常工作。)

如果不行的话,我可以让我的--complete选项通过匹配输入来改变回复的大小写,但最理想的情况是命令行能够尽可能地匹配数据库。

请注意,在使用readline时,我在应用程序内部已经使其正常工作了,只有与bash进行接口时才会出现问题。

2个回答

6

事实上,似乎没有办法让compgen针对单词列表(-W)进行不区分大小写的匹配。

以下是一些解决方法:

简单解决方案:首先将单词列表和输入令牌全部转换为小写字母。 注意:只有在“所有完成内容都变成小写字母”这种情况下才能使用此选项。

complete_lower() {

    local token=${COMP_WORDS[$COMP_CWORD]}
    local words=$( db --complete $COMP_CWORD "${COMP_WORDS[@]}" )

    # Translate both the word list and the token to all-lowercase.
    local wordsLower=$( printf %s "$words" | tr [:upper:] [:lower:] )
    local tokenLower=$( printf %s "$token" | tr [:upper:] [:lower:] )

    COMPREPLY=($(compgen -W "$wordsLower" -- "$tokenLower"))   
}

更好的解决方案是:自己编写不区分大小写的匹配逻辑。
complete_custommatch() {

    local token=${COMP_WORDS[$COMP_CWORD]}
    local words=$( db --complete $COMP_CWORD "${COMP_WORDS[@]}" )

    # Turn case-insensitive matching temporarily on, if necessary.
    local nocasematchWasOff=0
    shopt nocasematch >/dev/null || nocasematchWasOff=1
    (( nocasematchWasOff )) && shopt -s nocasematch

    # Loop over words in list and search for case-insensitive prefix match.
    local w matches=()
    for w in $words; do
        if [[ "$w" == "$token"* ]]; then matches+=("$w"); fi
    done

    # Restore state of 'nocasematch' option, if necessary.
    (( nocasematchWasOff )) && shopt -u nocasematch

    COMPREPLY=("${matches[@]}")
}

你应该写成 COMPREPLY=("${matches[@]}"),否则它将会在多词选项(例如文件名)中断开。 - Orwellophile
谢谢,@Orwellophile;我已经更新了我的答案。即使是单个单词选项 - 这是典型的情况和手头的情况 - 双引号也是一个好主意,以防止像'*'和'?'这样的字符的意外shell扩展。 - mklement0
3
真相是,引号对于抨击而言就像避孕套对于性行为一样。当你知道可以不用它们时,会更加刺激。而且当你犯错时,不一定能立即发现。 - Orwellophile
2
不要把shopt搞砸了,你可以使用一些bash魔法:"${w,,}"和"${token,,}"。 :) - Alexey Voinov
1
@AlexeyVoinov:谢谢;是的,如果你使用的是bash **4+**(例如OSX仍在使用bash 3.x),那就是一个选项。 - mklement0
显示剩余2条评论

2

使用grep更容易完成所有工作;这样,大小写就会在补全中保留下来,您不必去处理shopt或其他任何东西。例如:

_example_completions () {
    local choices="john JAMES Jerry Erik eMIly alex Don donald donny@example.com RON"
    COMPREPLY=( $( echo "$choices" | tr " " "\n" | grep -i "^$2" ) )
}

在这里,$choices 是你的单词列表,tr 用于将单词之间的空格改为换行符,以便 grep 可以理解它们(如果你的单词已经是按换行符分隔的,则可以省略该步骤),-i 选项是不区分大小写的匹配,"^$2" 匹配当前单词(bash 将其作为 $2 传递)在一行的开头。
$ example dO<tab>
Don     donald     donny@example.com

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