1

I am trying to autocomplete a folder name in a bash script. If I enter a complete folder name everything works, but I don't know how to autocomplete the name. Any ideas?

repo() {
 cd ~/Desktop/_REPOS/$1
}

I tried reading this SO post for ideas, but got lost quickly because I am still fairly new to bash.

GOAL (with the repo function above included in my bashrc):

>ls ~/Desktop/_REPOS/
folder1
hellofolder
stackstuff

>repo sta[TAB] fills in as: repo stackstuff

2 Answers 2

12

Actually you can borrow quite a bit of code from geirha's answer:

# this is a custom function that provides matches for the bash autocompletion
_repo_complete() {
    local file
    # iterate all files in a directory that start with our search string
    for file in ~/Desktop/_REPOS/"$2"*; do
        # If the glob doesn't match, we'll get the glob itself, so make sure
        # we have an existing file. This check also skips entries
        # that are not a directory
        [[ -d $file ]] || continue

        # add the file without the ~/Desktop/_REPOS/ prefix to the list of 
        # autocomplete suggestions
        COMPREPLY+=( $(basename "$file") )
    done
}

# this line registers our custom autocompletion function to be invoked 
# when completing arguments to the repo command
complete -F _repo_complete repo

The for loop iterates all files in a directory that start with the string given as second argument to the _repo_complete function (this is the string to be autocompleted).

Add the code to your .bashrc and it should work!

Sign up to request clarification or add additional context in comments.

4 Comments

If I wanted to remove the repo() function and put the code inside _repo_complete (to get only one function), how would I do it?
can I just change the final line to complete -F _repo_complete?
I do not think that is practical, since the _repo_complete function gets invoked every time the autocomplete is attempted. You could try to hack around this by checking if there is already a valid directory name, but I think that mess is not worth saving one function.
You probably don't need to add it to .bashrc. As per this FAQ, user completion scripts are sourced from ~/.local/share/bash-completion/completions/; this directory can be created by the user. At least on Ubuntu, system completion scripts are generally stored in /usr/share/bash-completion/completions/.
8
+100

I see two options to get what you want, i.e., path autocompletion for a custom cd command, if I understand correctly.

  1. You can add the parent directory, ~/Desktop/_REPOS, to your CDPATH environment variable:

    CDPATH="$CDPATH":"$HOME"/Desktop/_REPOS
    

    Now, from any directory, you can type cd SpaceTab, and in addition to the subdirectories of your current directory, all the directories in ~/Desktop/_REPOS will show up. (Which is also the drawback of this method: more clutter.)

  2. You can add a completion function to your .bashrc. The way you've started, you want the basenames of all the directories in ~/Desktop/_REPOS. To get autocompletion for directories, we can use the compgen -d builtin:

    $ compgen -d "$HOME"/Desktop/_REPOS/
    /home/me/Desktop/_REPOS/folder1
    /home/me/Desktop/_REPOS/folder2
    /home/me/Desktop/_REPOS/stackstuff
    /home/me/Desktop/_REPOS/hellofolder
    

    This returns the names of all subdirectories. It reduces to fewer candidates when the path is more specific:

    $ compgen -d "$HOME"/Desktop/_REPOS/f
    /home/me/Desktop/_REPOS/folder1
    /home/me/Desktop/_REPOS/folder2
    

    To remove everything but the basenames, we use shell parameter expansion, like this:

    $ arr=(/path/to/dir1 /path/to/dir2)
    $ echo "${arr[@]##*/}"
    dir1 dir2
    

    ##*/ in the parameter expansion removes the longest possible match of */ from each element of arr, i.e., leaves only what's after the last forward slash – exactly what we want.

    Now we put this together and into a function:

    _comp_repo () {
        # Get list of directories
        # $2 is the word being completed
        COMPREPLY=($(compgen -d "$HOME"/Desktop/_REPOS/"$2"))
    
        # Reduce to basenames
        COMPREPLY=("${COMPREPLY[@]##*/}")
    }
    

    This goes into your .bashrc, together with the instruction to use it for autocompletion with repo:

    complete -F _comp_repo repo
    

    Notice that your repo function should quote the $1 argument to make sure it handles directory names with special characters (spaces, tabs...) properly:

    repo () {
        cd ~/Desktop/_REPOS/"$1"
    }
    

The number of times you have hit Tab depends on readline settings such as show-all-if-ambiguous.


References

7 Comments

You probably don't need to add it to .bashrc. As per this FAQ, user completion scripts are sourced from ~/.local/share/bash-completion/completions/; this directory can be created by the user. At least on Ubuntu, system completion scripts are generally stored in /usr/share/bash-completion/completions/.
@Acumenus You're right - one caveat: this requires bash-completion to be installed. Adding the completion function to ~/.bashrc guarantees that it works even without bash-completion installed. The completions specified in /usr/share/bash-completion/completions and $XGD_DATA_HOME/bash-completion/completions have the big advantage that they are loaded dynamically the first time they're required; a disadvantage is that it requires a new-ish version of bash-completion, and the system path isn't the same everywhere (macOS: /usr/local/opt/bash-completion@2/share/bash-completion/completions).
Bash-completion also understands a few fallback locations: all files in /etc/bash_completion.d (this is where most homebrew packages stick their completions), and ~/.bash_completion, both of which are sourced in any case rather than loaded dynamically, but still keep ~/.bashrc a bit more clutter-free.
And lastly, if you know that bash-completion is installed, you can take advantage of its helper functions such as _init_completion, which are very robust argument parsers and used in all the completions provided by bash-completion itself - again, requiring that it is installed.
I expect most modern Linux distros to have bash-completion installed.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.