Bash – Is there a way to customize “Programmable Completion” in the current session

autocompletebash

If I start typing a command or filename, I can use the tab key to auto-complete my string. Is there a way to change the auto-complete behavior for the current bash session?

For example

On pressing Tab Tab in the current session, only files that contain *.txt will be displayed.

How does the autocomplete function work? Is there a set of environment variables or something that can be adjusted to change the autocomplete behavior?

Best Answer

In the bash_completion mechanism it's the bash function _filedir_xspec() that is responsible for filename and dirname completions. This funtion can be found in the script /etc/bash_completion.

You can edit that function and add a line that contains your regex. For example use this function (it's a copy of the original function):

_filedir_xspec()
{
    local IFS cur xspec

    IFS=$'\n'
    COMPREPLY=()
    _get_comp_words_by_ref cur

    _expand || return 0

    # get first exclusion compspec that matches this command
    xspec=$( awk "/^complete[ \t]+.*[ \t]${1##*/}([ \t]|\$)/ { print \$0; exit }" \
        "$BASH_COMPLETION" )
    # prune to leave nothing but the -X spec
    xspec=${xspec#*-X }
    xspec=${xspec%% *}

    local -a toks
    local tmp

    toks=( ${toks[@]-} $(
        compgen -d -- "$(quote_readline "$cur")" | {
        while read -r tmp; do
            # see long TODO comment in _filedir() --David
            printf '%s\n' $tmp
        done
        }
        ))

    # Munge xspec to contain uppercase version too
    eval xspec="${xspec}"
    eval xspec="!*.txt" #<---- HERE add your regex, that's the only line changed
    local matchop=!
    if [[ $xspec == !* ]]; then
        xspec=${xspec#!}
        matchop=@
    fi
    [[ ${BASH_VERSINFO[0]} -ge 4 ]] && \
        xspec="$matchop($xspec|${xspec^^})" || \
        xspec="$matchop($xspec|$(printf %s $xspec | tr '[:lower:]' '[:upper:]'))"

    toks=( ${toks[@]-} $(
        eval compgen -f -X "!$xspec" -- "\$(quote_readline "\$cur")" | {
        while read -r tmp; do
            [ -n $tmp ] && printf '%s\n' $tmp
        done
        }
        ))
    [ ${#toks[@]} -ne 0 ] && _compopt_o_filenames
    COMPREPLY=( "${toks[@]}" )
}

Notice the new part at line 32:

eval xspec="!*.txt" 

This statement is later used in compgen to remove all entries, that do not match the regular expression. I wouldn't recommend to edit the file /etc/bash_completion. Instead make a new file with the content above and source the file if needed:

source /path/to/file

Now, all commands/applications in the shell, that where completed by the function _filedir_xspec(), complete now only with *.txt filenames.

Related Question