Reputation: 9765
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
Upvotes: 1
Views: 14762
Reputation: 52112
I see two options to get what you want, i.e., path autocompletion for a custom cd
command, if I understand correctly.
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.)
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
Upvotes: 8
Reputation: 638
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!
Upvotes: 12