bash completion with different options based on first value - bash

I'm currently trying to write a bash_completion script for one of our tools.
Was looking at the apt-get and chkconfig scripts for some help.
Basically what I want to do is get a different option selection based on the first value.
There can be more than one option to a value.
command <value1> <--option1> <--option2> ...
command <value2> <--option3> <--option4> ...
Looking at the apt-get script, it will return the same list of options for any first value.
So this is not what I need.
Here is what I got so far:
_supdeploy()
{
local cur prev opts cword
_init_completion || return
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="deploy destroy supported list fulllist status fullstatus getip shutdown powerOff powerOn"
case "${prev}" in
deploy)
if [[ "$cur" == -* ]]; then
if [[ $cword -eq 2 || $cword -eq 3 || $cword -eq 4 ]]; then
COMPREPLY=( $( compgen -W '--hostname --os --version' -- "$cur" ) )
fi
fi
return 0
;;
destroy)
if [[ "$cur" == -* ]]; then
COMPREPLY=( $( compgen -W '--name --silent' -- "$cur" ) )
fi
return 0
;;
*)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
esac
} &&
complete -F _supdeploy supdeploy
I get a different selection of options for both deploy and destroy.
But I can only use one -- option.
When I try to use -- again, nothing happens, and without the -- I get the list from opts.
It is probably something easy, but I can't find the error here at the moment.
I also have it tried without the $cword before, same result

Instead of prev, you just want to look at the value of the first argument each time:
case ${COMP_WORDS[1]} of
It gets a little tricker if you allow "general" options to precede the subcommand, but in general you want to look at the first non-option argument, not necessarily the previous argument.

Related

how to add custom completion to ls command in bash_completion?

i am trying for suggest custom options in bash completion in commands linux ( centos 7 )
i am know the this code add custom command to bash and suggest options
path : /etc/bash_completion.d/foo
_foo()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-i(--incoming) -o(-outgoing) -m(--missed) -a(-all) "
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -F _foo foo
foo -[tab]
-i(--incoming) -o(-outgoing) -m(--missed) -a(-all)
goal me is extend this source code to built in commands in linux such as ls
trying me is :
path : /etc/bash_completion.d/ls
_ls()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-a(--all) -h(--human-readable) -r(--reverse) "
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
complete -F _ls ls
when this code used not suggest folders and files complete in ls command
ls -[tab]
-a(--all) -h(--human-readable) -r(--reverse)
ls[tab]
not suggest files and directory
goal me is add custom option for suggest in bash and not behaviour command

Call bash-completion for another command from within completion function

_example()
{
local cur prev
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ ${prev} == example ]] ; then
COMPREPLY=( $(compgen -W "git" -- ${cur}) )
elif [[ ${prev} == git ]] ; then
_command
elif [[ ${prev} == g ]] ; then
# ??? TODO
# I want the same completion for `example git <TAB>` and `example g <TAB>`
else
COMPREPLY=()
fi
return 0
}
complete -F _example example
I know how to "call" bash-completion for next command. (code above)
eg.: example git <TAB> => completion for git
How to make bash-completion that "calls" any completion I want for next command?
eg.: example g <TAB> => completion for git
Populate COMP_CWORD, COMP_WORDS, and COMP_LINE, and then call git's completion function __git_wrap__git_main.
For an example of populating COMP_* and invoking a pre-defined completion function, see this.
For more on figuring out how git completes, you might want to start here.

bash complete with colon

I'm trying to add auto-complete function to mytool command with following _mytool complete function:
_mytool()
{
local cur
_get_comp_words_by_ref -n : cur
# my implementation here
COMPREPLY=() # Array variable storing the possible completions.
cur=${COMP_WORDS[COMP_CWORD]}
if [[ $cur = -* ]]; then
COMPREPLY=( $(compgen -W '-a:first -a:second' -- $cur) )
fi
__ltrim_colon_completions "$cur"
}
complete -F _mytool mytool
Because there is ":" in my COMPREPLY, I try to make it work by using _get_comp_words_by_ref and __ltrim_colon_completions function, which is learnt from: here
But it still not work when type:
$mytool -a:[TAB]
there is no auto-complete at all. I was expecting bash will print the following completion for me:
-a:first -a:second
my bash version:
4.3.46(1)-release
What am I missing? Thanks!

Bash filename autocompletion after switch/parameter

I tried this other questions's accepted answer but it doesn't work for me. So please don't vote this as duplicate.
My script is named "tracker" and it accepts the following switches: --dummy --audit_sessiones --user_count --detailed_user_count --parfile
The --parfile switcj should be followed by a filename.
I have this autocompletion script:
_tracker()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="--dummy --audit_sessiones --user_count --detailed_user_count --parfile"
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
opts="ls *"
if [[ ${prev} == --parfile ]]; then
COMPREPLY=( "${files[#]##*/}" )
return 0
fi
}
complete -F _tracker tracker
Autocompletion of switches works fine.
But I also want the user to be able to use filename autocompletion right after the parameter --parfile but I haven't been able to make it work.
complete has a -o default option so you can remove the opts="ls *"; if ... fi part and just do complete -F _tracker -o default tracker.
According to bash manual:
If the -o default option was supplied to complete when
the compspec was defined, readline's default completion will
be performed if the compspec (and, if attempted, the default
bash completions) generate no matches.
Try replacing COMPREPLY=( "${files[#]##*/}" ) with COMPREPLY=( $(compgen -f ${cur}) )
More information about auto completion can be found in the following links
An introduction to bash completion: part 1
An introduction to bash completion: part 2

Shell custom autocomplete - not to list previous listed arguments

I have programmed auto completion to my script but when I enter TAB is is showing the previous filled arguments also. how to avoid this.
here is my code:
_load_opt ()
{
COMPREPLY=()
local cur=${COMP_WORDS[COMP_CWORD]}
local prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
"--create")
COMPREPLY=( $( compgen -W "--name --type --size" -- $cur ) )
return 0
;;
"--delete")
COMPREPLY=( $( compgen -W "--name" -- $cur ) )
return 0
;;
esac
if [[ ${cur} == -* ]]
then
COMPREPLY=($(compgen -W "--create --delete" -- $cur ) )
return 0
fi
}
complete -F _load_opt ./run.sh `
i sourced this script.
and when i run
# ./run.sh --create --name file.txt --
--create --delete
Because of the last default if statement it is auto completing the main arguments. But I want to auto complete --type --size and NOT --name again.
I tried to add one more case with --create --name.But I should go add with all combinations. That doesn't sounds correct.
How can I achieve this?. Thanks for the help.
To do what you want, you'll need to examine the entire command line, one option at a time, something like this (not tested much): (Edit: uses associative arrays, for which you need bash v4)
_load_opt () {
# If we're at the beginning, only allow the verb options.
if (( COMP_CWORD == 1 )); then
COMPREPLY=($(compgen -W "--create --delete" -- "$2"));
fi;
if (( COMP_CWORD <= 1 )); then
return;
fi;
# Otherwise, construct the list of allowed options based on the verb
local -A flags;
case ${COMP_WORDS[1]} in
--create)
flags[--name]=ok;
flags[--type]=ok;
flags[--size]=ok
;;
--delete)
flags[--name]=ok
;;
*)
return 0
;;
esac;
# And scan the entire command line, even after the current point, for already
# provided options, removing them from the list
local word;
for word in "${COMP_WORDS[#]:2}";
do
unset flags[$word];
done;
# Finally, complete either an option or an option value, depending on the
# previous word (always the third argument to this function)
# The first three lines in the case statement are just examples
case $3 in
--name) COMPREPLY=($(compgen -W "valid name list" -- "$2")) ;;
--type) COMPREPLY=($(compgen -W "good bad ugly" -- "$2")) ;;
--size) COMPREPLY=($(compgen -W "small medium large" -- "$2")) ;;
*) COMPREPLY=($(compgen -W "${!flags[*]}" -- "$2")) ;;
esac
}

Resources