9

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?

2

3 Answers 3

10
+50

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.

2
  • Cool... What about making it only be for the current session? Jun 10, 2014 at 14:35
  • 2
    it IS only for the current session. You can copy the function into your shell and it will be away in the next session. But I recommend to source a file that contains the function when you need it. In the next session it's not anymore available. To make it permanent, I would write it one of the rc scripts: ~/.bashrc for example.
    – chaos
    Jun 10, 2014 at 16:49
2

You can also use this only for certain programs, or your custom script or command.

For showing only files that have the txt extension in the suggestion, simply write a shell script and source in the current session:

# Autocomplete file /path/to/file/sample-completion.sh
# for the command `sample`
_sample(){
   COMPREPLY=($(compgen -f -X "!.*[.][tT][xX][tT]$" -- "$2"))
}

complete -F _sample sample

And source it by calling:

$ source /path/to/file/sample-completion.sh

In this code, $2 is the current word being completed. The -- "$2" part makes compgen filter out only the suggestions that contain the current word being completed and put it into COMPREPLY. Also, the -f option tells compgen to only look for files in the current directory. Last but not least the -X option is a filter pattern (look here) that excludes matching file names. The ! in the beginning of pattern negates this behavior. As a result, only the files with .txt extension will be shown. The $ sign at the end of the regex looks for matches that end with given pattern. The [.] means it will look for a literal dot, not just any character - which is the default meaning of a dot in regex. The [tT][xX][tT] part means the pattern will match all these situations: "txt, txT, tXt, tXT, Txt, TxT, TXT, TXt".

0

Leaning on the accepted answer from @chaos, I propose this function to patch the existing completion function when needed:

patchFDxspec(){
  . <(type _filedir_xspec | sed -e '1d;s/\(eval xspec="\).*$/&\n\1!*.txt";/');
}

Executing this function will patch _filedir_xspec for the current session.

If you need other extensions than .txt, you could modify the function to take the wanted suffix as $1.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .