Create bash completion script to autocomplete paths after is-equal sign?

I want to create a bash completion script which recognises arguments of the form --arg and --some-arg=file.

After reading this tutorial and some examples in /usr/share/bash_completion/completions/, I’ve written the following script (to save time at typing some flags with Chromium):

_chromium() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Some interesting options
    opts="
--disable-web-security
--easy-off-store-extension-install
--incognito
--load-extension=
--pack-extension=
--pack-extension-key=
--user-data-dir=
"
    # Handle --xxxxxx=file
    if [[ ${cur} == "--"*"=" ]] ; then
        # Removed failures (is my logic OK?)
        return 0
    fi

    # Handle other options
    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}
complete -F _chromium chromium

I saved it to ~/bash_completions/chromium, and created a symlink using sudo ln -s ~/bash_completions/chromium /usr/share/bash_completion/completions/chromium.
Then I loaded it using . /usr/share/bash_completions/completions/chromium.

Now, I’m experiencing two problems:

  • chromium --u<TAB> expands to chromium --user-data-dir=<SPACE> (I don’t want the space).
  • Paths (directories and files) are not completed any more.

How can I solve these problems?

Asked By: Rob W

||

To complete filenames try passing -f to compgen.

I’m afraid you won’t be able to get rid of the spaces after the options, since that’s how completion works – once it finds a unique match, it completes it completely.

Answered By: peterph

I have found the solution to both problems!

  1. To not append a space, use the nospace option. This can be done in two ways:

    • Pass it to the complete:
      complete -o nospace -F _chromium chromium
    • Use the compopt built-in:
      compopt -o nospace (to enable the option)
      compopt +o nospace (to disable the option)

    I found it in the Bash documentation at gnu.org, 8.7 Programmable Completion Builtins.

  2. Completion of files.
    • peterph suggested to use the -f flag with compgen. I followed that advice, and implemented it as COMPREPLY=( $(compgen -f "$cur") ). This worked fine, until I tried to complete a path containing spaces.
      On Stack Overflow, I found this answer which recommends manual creation of the completion list (without using compgen). This approach worked fine.
    • Use the filenames option to tell Readline that the compspec generates filenames, so it can:
      • perform any filename-specific processing (like adding a slash to directory names, quoting special characters, or suppressing trailing spaces)
      • use different colors to indicate file type (with colored-stats enabled)

With aid of Bash’s string manipulation (to expand ~ and deal with spaces), I constructed a bash completion scripts which meets all criteria stated in the question.

_chromium() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Some interesting options
    opts="
--disable-web-security
--easy-off-store-extension-install
--incognito
--load-extension=
--pack-extension=
--pack-extension-key=
--user-data-dir=
"
    # Handle --xxxxxx=
    if [[ ${prev} == "--"* && ${cur} == "=" ]] ; then
        compopt -o filenames
        COMPREPLY=(*)
        return 0
    fi
    # Handle --xxxxx=path
    if [[ ${prev} == '=' ]] ; then
        # Unescape space
        cur=${cur//\ / }
        # Expand tilder to $HOME
        [[ ${cur} == "~/"* ]] && cur=${cur/~/$HOME}
        # Show completion if path exist (and escape spaces)
        compopt -o filenames
        local files=("${cur}"*)
        [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]// / }" )
        return 0
    fi

    # Handle other options
    COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
    if [[ ${#COMPREPLY[@]} == 1 && ${COMPREPLY[0]} != "--"*"=" ]] ; then
        # If there's only one option, without =, then allow a space
        compopt +o nospace
    fi
    return 0
}
complete -o nospace -F _chromium chromium
Answered By: Rob W
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.