-
Notifications
You must be signed in to change notification settings - Fork 0
Gtransfer's bash completion
For reference please open the gtransfer bash completion script in another window or tab of your browser.
For writing bash completion scripts for arbitrary tools I recommend to have a look at already existing scripts in your favorite Linux/Unix distribution (e.g. Debian and Ubuntu already have a lot of bash completion scripts available).
NOTE: First some introductory words. A shell commandline usually consists of the command (possibly with a path prefix) and the options of the command (usually separated by a whitespace character). Together these make up the individual words of a command line. In general bash completion makes suggestions (i.e. possible completions) for the current word.
To create your own bash completion script you need at least one (completion) function, usually named like the corresponding tool and a _
as prefix (e.g. _gtransfer()
). In its simplest form this function will just add elements (words) to the global array variable COMPREPLY
, like in the following example:
_tool()
{
COMPREPLY=( "-o" "-p" "-t" "-s" )
}
These words will then be proposed after hitting the TAB
key.
For a more advanced completion you can make use of the special bash completion variables COMP_WORDS
and COMP_CWORD
:
From Bash Variables
-
COMP_CWORD
: An index into${COMP_WORDS}
of the word containing the current cursor position. This variable is available only in shell functions invoked by the programmable completion facilities. -
COMP_WORDS
: An array variable consisting of the individual words in the current command line. The line is split into words as Readline would split it, usingCOMP_WORDBREAKS
as described above. This variable is available only in shell functions invoked by the programmable completion facilities
For example to get the word at the current cursor position you can use:
local cur
cur="${COMP_WORDS[COMP_CWORD]}"
Or to get the word before the current cursor position (e.g. to complete an option argument, like in -s gsiftp://[...]
) you can use:
local prev
prev="${COMP_WORDS[COMP_CWORD-1]}"
After the completion function you need to add a call to complete
like the following one:
complete -o nospace -F _gtransfer gtransfer gt
-
-o nospace
prevents the appending of a whitespace after the completion. This option spares a user to hitBACKSPACE
if he wants to continue with the completion of the current word (e.g. when traversing a remote directory tree with gtransfer). -
-F _gtransfer
specifies the completion function to use - the following non-option arguments specify the tools the completion should be applied to (here we use the
gtransfer
tool and its shortcutgt
)
_gtransfer()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=( [...] )
return 0
}
complete -o nospace -F _gtransfer gtransfer gt
For more details about the shown bash builtins have a look at the Bash Reference Manual.
Option completion is very easy to accomplish with bash completion. All that is needed for gtransfer is to prepare a variable (e.g. named opts
) containing all options separated by a whitespace character and a compgen
commandline as shown in the following example:
_gtransfer()
{
local cur
cur="${COMP_WORDS[COMP_CWORD]}"
[...]
# all available gtransfer options/switches/parameters
local opts="--source -s --destination -d --help --verbose -v --version -V --metric -m --logfile -l --auto-clean -a --configfile --"
[...]
# complete possible gtransfer options/switches/parameters
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
}
complete -o nospace -F _gtransfer gtransfer gt
Completion of host URL parts is done with the help of the dpath
utility. For this specific feature, the dpath utility has two specific options:
--list-sources
--list-destinations
Executing dpath {--list-sources|--list-destinations}
will print out all unique source/destination host URL parts (i.e. gsiftp://host.domain.tld:<PORT>
) available in both system and user dpaths. But because the completion should only happen when the gtransfer options -s|--source
and -d|--destination
are used, the completion script also has to take this into account. This is done by using a case
clause and a word match with the word before the current word:
_gtransfer()
{
[...]
prev="${COMP_WORDS[COMP_CWORD-1]}"
[...]
# parameter completion
case "${prev}" in
--source|-s)
[...]
# only complete destination URLs if dpath is available
if hash dpath &>/dev/null; then
# complete source URLs
local sites=$( dpath --list-sources )
COMPREPLY=( $(compgen -W "${sites}" -- ${cur}) )
return 0
fi
;;
--destination|-d)
[...]
# only complete destination URLs if dpath is available
if hash dpath &>/dev/null; then
# complete destination URLs
local sites=$( dpath --list-destinations )
COMPREPLY=( $(compgen -W "${sites}" -- ${cur}) )
return 0
fi
;;
[...]
esac
[...]
}
complete -o nospace -F _gtransfer gtransfer gt
Completing remote paths requires access to the remote file system. Currently the globus-url-copy
(guc) tool is used to provide this. When using the specific option -list
and a valid URL (e.g. gsiftp://host.domain.tld:<PORT>/~/*
), guc will print out a listing of the remote directory referenced by the URL, which would be the user's home directory in this case. Such a listing looks like the following example:
$ guc -list gsiftp://[email protected]:2811/home/johndoe/
gsiftp://[email protected]:2811/home/johndoe/
.bash_history
.bash_logout
.bashrc
.profile
my_source_dir/
As this contains some superfluous strings (the first line and multiple spaces before each file), these are removed by a sed
command before further processing:
sed -e 's/^\ *//' -e 1d
In order to complete remote paths we need to do a listing for the current word after a gtransfer -s|--source
or -d|--destination
option, as shown in the following example (only for -s|--source
, because it's similar for -d|--destination
):
_gtransfer()
{
[...]
prev="${COMP_WORDS[COMP_CWORD-1]}"
[...]
# parameter completion
case "${prev}" in
--source|-s)
# only complete remote paths if globus-url-copy is
#+ available
if hash globus-url-copy &>/dev/null; then
# complete remote paths
if echo "$cur" | grep '^gsiftp://.*:.*/.*' &>/dev/null; then
userhost=$( getURLWithoutPath "${cur}" )
userpath=$( getPathFromURL "${cur}" )
local remote_paths=( $( globus-url-copy -list "$cur*" | sed -e 's/^\ *//' -e 1d ) )
local remote_urls=$( for path in "${remote_paths[@]}"; do echo ${userhost}${userpath}${path}; done )
COMPREPLY=( $(compgen -W "${remote_urls}" -- ${cur}) )
return 0
fi
fi
[...]
;;
[...]
esac
[...]
}
complete -o nospace -F _gtransfer gtransfer gt