Yes, it is possible to implement path-completion from a custom base-directory specified by a command-line option-flag. Here is a small example illustrating how to accomplish this. First I'm going to slightly modify your example script to make the demonstration slightly more interesting (i.e. to produce output):
#!/bin/bash
# echo_path.sh
#
# Echoes a path.
#
# Parse command-line options
while getopts ":d:" opt; do
# Use '-d' to specify relative path
case "${opt}" in
d)
directory="${OPTARG}"
;;
esac
done
# Print the full path
echo "$(readlink -f ${directory})"
This is essentially the same as your example script, but it prints out the given argument.
Next we need to write a function to be called by the Bash programmatic-completion system. Here is a script which defines such a function:
# echo_path_completion.bash
# Programmatic path completion for user specified file paths.
# Define a base directory for relative paths.
export BASE_DIRECTORY=/tmp/basedir
# Define the completion function
function _echo_path_completion() {
# Define local variables to store adjacent pairs of arguments
local prev_arg;
local curr_arg;
# If there are at least two arguments then we have a candidate
# for path-completion, i.e. we need the option flag '-d' and
# the path string that follows it.
if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
# Get the current and previous arguments from the command-line
prev_arg="${COMP_WORDS[COMP_CWORD-1]}";
curr_arg="${COMP_WORDS[COMP_CWORD]}";
# We only want to do our custom path-completion if the previous
# argument is the '-d' option flag
if [[ "${prev_arg}" = "-d" ]]; then
# We only want to do our custom path-completion if a base
# directory is defined and the argument is a relative path
if [[ -n "${BASE_DIRECTORY}" && ${curr_arg} != /* ]]; then
# Generate the list of path-completions starting from BASE_DIRECTORY
COMPREPLY=( $(compgen -d -o default -- "${BASE_DIRECTORY}/${curr_arg}") );
# Don't append a space after the command-completion
# This is so we can continue to apply completion to subdirectories
compopt -o nospace;
# Return immediately
return 0;
fi
fi
fi
# If no '-d' flag is given or no base directory is defined then apply default command-completion
COMPREPLY=( $(compgen -o default -- "${curr_arg}") );
return 0;
}
# Activate the completion function
complete -F _echo_path_completion echo_path
Now let's source our completion script:
source echo_path_completion.bash
Let's make our script executable and move it to somewhere in our PATH:
chmod +x echo_path.bash
mv -i echo_path.bash /usr/local/bin
And finally, let's add an alias for our script which doesn't have a file extension:
alias echo_path=echo_path.bash
Now if you enter echo -d
and hit the tab button then you should get file-path completion starting from the BASE_DIRECTORY. To test this out you could try the following:
mkdir -p ${BASE_DIRECTORY}
mkdir -p "${BASE_DIRECTORY}/file"{1..5}
When you hit tab you should get the following list of completions:
user@host:~$ echo_path -d
user@host:~$ echo_path -d /tmp/basedir/file
/tmp/basedir/file1 /tmp/basedir/file2 /tmp/basedir/file3 /tmp/basedir/file4 /tmp/basedir/file5
Notice that after the first tab the string is converted to an absolute path. You can change this if you want, but I think this is might be the preferable behavior.
Here are some references you can consult for more information.
For official references, take a look at following sections of the Bash manual:
Also look at the Advanced Bash Scripting Guide from the Linux Documentation Project:
For a quick introduction to some features of programmatic completion in Bash, see this post from "The Geek Stuff" site:
There are also several related StackOverflow posts that you might find useful: