Conditional trailing space with bash programmable completion - bash

I'm creating a function to provide programmable completion for a command that I use (with much help from http://www.debian-administration.org/articles/317). The shell script usage is as follows:
script.sh command [command options]
where command can be either 'foo' or 'bar' and command options for 'foo' are 'a_foo=value' and 'b_foo=value' and command options for 'bar' are 'a_bar=value' and 'b_bar=value'.
Here's the configuration I'm using:
_script() {
local cur command all_commands
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
command="${COMP_WORDS[1]}"
all_commands="foo bar"
case "${command}" in
foo)
COMPREPLY=( $(compgen -W "--a_foo --b_foo" -- ${cur}) ); return 0;;
bar)
COMPREPLY=( $(compgen -W "--a_bar --b_bar" -- ${cur}) ); return 0;;
*) ;;
esac
COMPREPLY=( $(compgen -W "${all_commands}" -- ${cur}) )
return 0
}
complete -F _script script.sh
This mostly works as I'd like:
% script.sh f[TAB]
completes to:
% script.sh foo
(with a trailing space as desired)
However, this:
% script.sh foo a[TAB]
completes to:
% script.sh foo a_foo
(also with a trailing space)
I'd like to replace the trailing space with an '='. Alternatively, I'd be willing to change the values passed to compgen to be "--a_foo= --b_foo=", in which case I could just delete the trailing space.
Unfortunately, the command is not under my control, so I can't change the command line options to be of format "--a_foo value" instead of "--a_foo=value".

First you need to add = to the COMPREPLY:
COMPREPLY=( $(compgen -W "--a_foo= --b_foo=" -- ${cur}) )
next you need to tell completion not to add space after = with
compopt -o nospace
So, you script lines should be:
foo)
COMPREPLY=( $(compgen -W "--a_foo= --b_foo=" -- ${cur}) ); compopt -o nospace; return 0;;
bar)
COMPREPLY=( $(compgen -W "--a_bar= --b_bar=" -- ${cur}) ); compopt -o nospace; return 0;;

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

customize bash autocompletion

How to customize bash autocomplete to list the files in another directory for only one script option (-seq), for other script options (-speed, -define) default autocomplete is O.K. this is what I have
export files=`ls /home/tests/`
echo $files #debug
_xtest () { .
local cur
COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]}
#case "$cur" in
COMPREPLY=( $(compgen -W "${files}" -- ${cur}) )
# esac
return 0
}
complete -F _xtest -o filenames xtest
When I try to run from shell I get the below message before the list of files
> xtest -seq [TAB][TAB]
bash: .: filename argument required
.: usage: . filename [arguments]
Is there a way not to receive this message before the file list ?
How to enable default bash completion for other options ?
should use $prev instead of $cur :
_xtest () {
local prev
COMPREPLY=()
prev=${COMP_WORDS[COMP_CWORD-1]}
case "$prev" in
-seq )
COMPREPLY=( $(compgen -W "${files}" -- ${cur}) ) ;;
esac
return 0
}
complete -F _xtest -o filenames xtest

Complete command options with quoted values

I am trying to write a Bash completion function for completing long command line options like --param1=value1. This works fine if value1 is not quoted. But in many cases value1 needs to be quoted, for example, --param1='Hello world'. In this case, Bash completion stops working. For example:
_myprog()
{
local cur="${COMP_WORDS[$COMP_CWORD]}"
local words=(--param1 --param2)
COMPREPLY=( $(compgen -W "${words[*]}" -- "$cur") )
}
complete -F _myprog myprog
If I source this source script.sh and then type myprog --param1='hello' <tab><tab> nothing happens. It works fine if I start the quote before the double dashes, like myprog '--param1=hello' <tab><tab>..
Any suggestions?
This can be done using COMP_LINE instead of COMP_WORDS:
_myprog()
{
local comp_line=( $COMP_LINE )
local cur
if [[ ${COMP_LINE: -1} == ' ' ]] ; then
cur=""
else
cur="${comp_line[#]: -1}"
fi
local words=(--param1 --param2)
COMPREPLY=( $(compgen -W "${words[*]}" -- "$cur") )
}

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