bash-completion - completion function defined the first time a command is invoked
Asked Answered
M

3

4

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 ?
Maund answered 7/4, 2013 at 2:34 Comment(0)
A
7

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
Auxiliary answered 7/4, 2013 at 5:0 Comment(2)
Wow! didn't know bash had an option load new completions on-the-fly. You're right, I see a _completion_loader() function defined in /usr/share/bash-completion/bash_completion which seems to source all the files in /usr/share/bash-completion/bash_completion and hence the completions for ssh/scp get loaded. Sweet! :) I think for my usage case, I'll override and define my own completion loader to define the intended completions, and then call the one in bash-completion.Maund
I don't get how to use this. I tried running the code above. But when I type d rm <TAB>, I still get the error bash: completion: function '_docker' not found. (Ubuntu 16.04, Bash 4.3.48)Melvinamelvyn
M
4

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

Midlothian answered 19/5, 2014 at 22:38 Comment(0)
M
1

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

Melvinamelvyn answered 26/7, 2018 at 22:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.