Question

I've added a new alias scp_using_rsync, which uses rsync to copy files over SSH with certain options. I wanted to link the bash completion for scp to this alias.

It works when I add this line:

complete -o bashdefault -o default -o nospace -F _scp scp_using_rsync 2>/dev/null || complete -o default -o nospace -F _scp scp_using_rsync

The only problem is that I notice, _scp gets defined in my bash environment, only after I try tab-completion with ssh/scp at least once in that shell. So if I directly run scp_using_rsync in a new shell, I would get the _scp not found error.

The output from typeset -F in a new shell before and after trying tab completion for ssh or scp commands indicate clearly that the following functions get defined after trying tab-completion for the first time:

$ diff ~/.scratch/file1 ~/.scratch/file2
224a225,227
> declare -f _scp
> declare -f _scp_local_files
> declare -f _scp_remote_files
226a230
> declare -f _sftp
230a235,240
> declare -f _ssh
> declare -f _ssh_ciphers
> declare -f _ssh_macs
> declare -f _ssh_options
> declare -f _ssh_suboption
> declare -f _ssh_suboption_check

These functions seem to be defined in /usr/share/bash-completion/completions/ssh in my system.

These are my 2 inter-related questions:

  • How does bash figure out where to pick up the definitions automatically and define them when the completion is tried for the first time ?
  • How should I be linking the bash-completion for scp_using_rsync to scp's bash completion in a similar way ?
Was it helpful?

Solution

Bash 4.1 added a new -D option for complete, compgen and compopt:

New complete/compgen/compopt -D option to define a `default' completion: a completion to be invoked on command for which no completion has been defined. If this function returns 124, programmable completion is attempted again, allowing a user to dynamically build a set of completions as completion is attempted by having the default completion function install individual completion functions each time it is invoked.

There's an example from bash's manual:

_completion_loader()
{
     . "/etc/bash_completion.d/$1.sh" >/dev/null 2>&1 && return 124
}
complete -D -F _completion_loader

OTHER TIPS

I had an issue applying whjm's answer. As Tuxdude noticed, there was already a function _completion_loader defined in my distribution (Ubuntu 14.04). The function was defined as follows:

_completion_loader () 
{ 
    local compfile=./completions;
    [[ $BASH_SOURCE == */* ]] && compfile="${BASH_SOURCE%/*}/completions";
    compfile+="/${1##*/}";
    [[ -f "$compfile" ]] && . "$compfile" &> /dev/null && return 124;
    complete -F _minimal "$1" && return 124
}

I wanted to make keep the maintenance of the completions all in one place, so I added a file to /usr/share/bash-completion/completions/ with the name of the command I wanted to add completion for. For your case, you could add a file called scp_using_rsync containing this:

cfile="${compfile%/*}/scp"
cmd="${1##*/}"
. "$cfile" 
complete -F _scp $cmd

This will source the file that defines completions for scp, including the _scp function, and then add the completion for your command. I felt this is a more direct and consistent way (though of course to do this, you must be ok with changing this behavior for all users.)

Some completion functions are dynamically loaded. You can manually load them so that they will work with aliases. For example, you can add this to your ~/.bashrc so that completion will work with your alias.

_completion_loader docker

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top