Skip to content

Gtransfer's bash completion

fr4nk5ch31n3r edited this page Dec 25, 2012 · 4 revisions

Gtransfer's bash completion

For reference please open the gtransfer bash completion script in another window or tab of your browser.

Bash completion boilerplate code

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.

Completion function

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.


Special bash completion variables

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, using COMP_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]}"

Call to complete

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 hit BACKSPACE 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 shortcut gt)

The full example

_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.

Completion of options

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

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

Completion of remote paths

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