Search code examples
bashbash-completion

bash tab completion with spaces


I'm having a problem with bash-completion when the possible options may contain spaces.

Let's say I want a function which echoes the first argument:

function test1() {
        echo $1
}

I generate a list of possible completion options (some have spaces, some not), but I don't manage to handle spaces correctly.

function pink() {
    # my real-world example generates a similar string using awk and other commands
    echo "nick\ mason syd-barrett david_gilmour roger\ waters richard\ wright"
}

function _test() {
    cur=${COMP_WORDS[COMP_CWORD]}
    use=`pink`
    COMPREPLY=( $( compgen -W "$use" -- $cur ) )
}
complete -o filenames -F _test test

When I try this, I get:

$ test <tab><tab>
david_gilmour  nick           roger          waters
mason          richard        syd-barrett    wright
$ test r<tab><tab>
richard  roger    waters   wright

which is obviously not what I meant.

If I don't assign an array to COMPREPLY, i.e. only $( compgen -W "$use" -- $cur ), I get it working if only one option remains:

$ test n<tab>
$ test nick\ mason <cursor>

But if several options remain, they are all printed within single quotes:

$ test r<tab><tab>
$ test 'roger waters
richard wright' <cursor>

There must be something wrong with my COMPREPLY variable, but I can't figure out what...

(running bash on solaris, in case that makes a difference...)


Solution

  • If you need to process the data from the string you can use Bash's built-in string replacement operator.

    function _test() {
        local iter use cur
        cur=${COMP_WORDS[COMP_CWORD]}
        use="nick\ mason syd-barrett david_gilmour roger\ waters richard\ wright"
        # swap out escaped spaces temporarily
        use="${use//\\ /___}"
        # split on all spaces
        for iter in $use; do
            # only reply with completions
            if [[ $iter =~ ^$cur ]]; then
                # swap back our escaped spaces
                COMPREPLY+=( "${iter//___/ }" )
            fi
        done
    }