5

I am trying to create a bash completion script for my command. ls has the behaviour that I want. I need to get this same behaviour form my command.

This is what I get for ls

$ ls /home/tux/<Tab><Tab>
Downloads  Documents  Pictures
$ ls /home/tux/Do<Tab><Tab>
Downloads  Documents

I.e. bash only shows the next paths relatively, not absolutely (i.e. it does add Downloads to the completion list, but not /home/tux/Downloads)

I wanted to write a completion script that works the same way. This is what I have tried.

_testcommand ()
{
    local IFS=$'\n'
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -o bashdefault -d -- "$cur") )
    if [ "${#COMPREPLY[@]}" -ne 1 ]
    then
        # remove prefix "$cur", so the preview of paths gets shorter
        local cur_len=$(echo $cur | sed 's|/[^/]*$||' | wc -c)
        for i in ${!COMPREPLY[@]}
        do
            COMPREPLY[$i]="${COMPREPLY[i]:$cur_len}"
        done
    fi
    return 0
}

complete -o nospace -F _testcommand testcommand

However the result is like this:

$ testcommand /home/tux/<Tab><Tab>
Downloads  Documents  Pictures
$ testcommand /home/tux/Do<Tab>
Downloads  Documents
$ testcommand Do

How can I make my completion not remove /home/tux/ from the command line?

Note: I think I can not add '-f' or '-d' etc. to the complete call on the bottom. In reality, the completion must, in some cases, also complete words instead of paths.

2 Answers 2

2
+50

You can check which completion function is used by "ls": complete -p | grep ls. And you can check the function with: type _longopt (the previous command's result). In the _longopt function you can find the _filedir function.

And finally an interesting article about completion: https://spin.atomicobject.com/2016/02/14/bash-programmable-completion/

2
  • Thanks, the complete -p was helpful to find a solution. As this is just a hint to how to get the solution, I've added a full example of how to do it. Adding "$1" does not really help, it only adds "testcommand" to the completion - have you tried it? If you fix/remove/explain this, I'll reward you with the bounty.
    – Johannes
    Aug 18, 2018 at 10:51
  • Sorry, you are right, my first solution didn't work. I tried it and it worked for me, but maybe I forgot to source the last version. (Now it works as you explained). I found the _filedir function but I supposed you wanted to write a custom function
    – csm
    Aug 19, 2018 at 19:59
0

Most programs in the bash completion dir (pkg-config --variable=completionsdir bash-completion) use the _filedir function provided by bash-completion itself. It seems legitimate to reuse _filedir - No need to worry about your own implementation!

MWE:

_testcommand()
{
    # init bash-completion's stuff
    _init_completion || return

    # fill COMPREPLY using bash-completion's routine
    # in this case, accept only MarkDown and C files
    _filedir '@(md|c)'
}
complete -F _testcommand testcommand

Of course, you can still use this while completing non-files:

if ...
then
    # any custom extensions, e.g. words, numbers etc
    COMPREPLY=( $(compgen ...) )
else
    # fill COMPREPLY using bash-completion's routine
    _filedir '@(md|c)'
fi

How did I find it?

Thanks to @csm: Using their answer, check type _longopt, which calls _filedir.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.