Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting case-insensitivity for bash completion on a command-by-command basis

Is there a way to specify that a particular command has case insensitivity, without turning on case insensitivity globally (at least for that shell)?

In my particular case, I have a small app that gives me command line access to a database of email addresses, so I type:

db get email john smith

and it returns back with John Smith's email address. So I've managed to enable completion largely inside the app: setting

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

works to allow me to tab-complete get and email. However, if I then type j<tab>, it refuses, because in the email database, it's properly capitalised. I'd like to get bash to complete this anyway. (If I use a capital J, it works.)

Failing that, I can have my --complete option change the case of its reply by matching the input, I suppose, but ideally the command line would match the database if at all possible.

Note that I have this working inside the app when using readline, it's only interfacing with bash that seems to be an issue.

like image 337
Tanktalus Avatar asked May 09 '12 23:05

Tanktalus


1 Answers

Indeed there seems to be no way to have compgen do case-insensitive matching against the word list (-W). I see the following workarounds:

Simple solution: Translate both the word list and the input token to all-lowercase first. Note: This is only an option if it's acceptable to have all completions turn into all-lowercase.

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"))   
}

Better, but more elaborate solution: Roll your own, case-insensitive matching logic:

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[@]}")
}
like image 76
mklement0 Avatar answered Sep 28 '22 04:09

mklement0