Reputation: 167
I have my own script extending scp
, with the same syntax. Naturally, I want to use the same autocomplete function. Following the suggestion in another question I added the following to .bash_aliases
complete -o nospace -F _scp my_scp
Unfortunately, if I type my_scp <tab> <tab>
in the terminal, I get the error bash: completion: function `_scp' not found
. If instead, I first type scp <tab> <tab>
then the autocompletion function is loaded, and I can then use my_scp
completion without a problem.
How do I ensure that the lazily-loaded function _scp
is loaded when it is needed for my custom command?
ADDED IN RESPONSE TO A COMMENT: Output of complete -p my_scp scp
before typing scp <tab> <tab>
in the terminal:
complete -o nospace -F _scp my_scp
bash: complete: scp: no completion specification
Output of complete -p my_scp scp
after typing scp <tab> <tab>
in the terminal:
complete -o nospace -F _scp my_scp
complete -o nospace -F _scp scp
Similarly, type _scp
gives bash: type: _scp: not found
before, and a long source code after.
Upvotes: 0
Views: 91
Reputation: 287
First we need to find out where the function _scp
is defined
complete -p scp
should show a function FUNC.shopt -s extdebug
then declare -F FUNC
which will print the path of the file FILE where the function is defined.Next we set it up as the completion function for our command my_scp
also with the same options which you seem to have done
A better way would be to trigger bash_completion to load the function the way it normally does that:
First without any error checks
use_lazy_loaded_bash_completion(){
local my_cmd=$1
local cmd=$2
__load_compltion "${cmd}"
local spec="$(complete -p "${cmd}")"
${spec} "${my_cmd}"
}
Assuming bash_completion <2.12, when it is loaded, bash_completion sets a default completion function
complete -D _completion_loader
which calls __load_completion
which sets up completion for the command name it receives as an argument.
We use complete -p
to get the completion specification, then run that line with an extra argument so we set the exact same completion specification for our command.
Now since bash_completion changed things up in 2.12,
_scp
is now _comp_cmd_scp
__load_completion
is now _comp_load
the following code works for both and has error checks:# Whatever the comp spec is for cmd, use the same comp spec for my_cmd
# For example, complete -p scp -> `complete -o bashdefault -F _comp_cmd_scp`
# Then we run `complete -o bashdefault -F _comp_cmd_scp scp my_scp`
# We don't need to parse anything, we just need to add another positional
# argument at the end of the complete command.
use_same_completion_spec(){
local cmd=$1
local my_cmd=$2
if declare -f __load_completion >/dev/null ; then
if ! __load_completion "${cmd}" ; then
echo "__load_completion failed to load completion for '${cmd}'" >&2
return 1
fi
elif declare -f _comp_load >/dev/null ; then
if ! _comp_load "${cmd}" ; then
echo "_comp_load failed to load completion for '${cmd}'" >&2
return 1
fi
else
echo "__load_completion and _comp_load do not exist" >&2
return 1
fi
if ! spec="$(complete -p "${cmd}")" ; then
echo "Error getting completion specification for '${cmd}'" >&2
fi
${spec} ${my_cmd}
}
And
# Demo for scp:
use_same_completion_spec scp my_scp
complete -p my_scp
should print complete -F _comp_cmd_sscp my_scp
(or -F _scp
depending on your version of bash-completion).
In the above code, we use declare -f FUNC
to check if a certain shell function is defined in the current environment. That way we can know which of the tow functions __load_completion
or _comp_load
we need to call. If declare -f FUNC
succeeds, that is like a "true" condition so we enter the if
.
So the first if-elif-else statement does this:
__load_completion
exist? If so try to call it and if that fails print a message and return 1 from the function._comp_load
exist? If so try to call it and output a message and return 1 if it fails.And if we made it this far, get the compspec for the original function:
something like complete -o bashdefault -F _comp_cmd_scp scp
and if we succeed, then we run what we got but with our function added to the end which would be
complete -o bashdefault -F _comp_cmd_scp scp my_scp
which sets _comp_cmd_scp
as the completion function for both scp
and my_scp
. We set the completion for scp
a second time this way but that's not a problem.
EDIT 1: Calling use_same_completion_spec
or any code that uses __load_completion
or anything from bash-completion should generally only be done in interactive shells in something like if [[ $- == *I* ]] ; then ... ; fi
since on most systems, the normal configuration for bash-completion is to have it only be loaded for interactive shells. To prepare this answer, I worked in a script that I ran non-interactively and at the top of that script, I loaded bash completion manually.
EDIT 2: In my code, I did some checks to support both __load_completion
and _comp_load
. For personal tools, I don't actually do this, I just use __load_completion
. But since the internet is forever, I wanted this answer to be future proof.
Upvotes: 2
Reputation: 26727
The function _scp
is defined in /usr/share/bash-completion/completions/scp
,
to load it, put following in .bash_aliases :
shopt -s extglob; source /usr/share/bash-completion/completions/scp
Upvotes: 1