Bash filename autocompletion after switch/parameter - bash

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

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

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 completion with different options based on first value

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.

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") )
}

custom directory completion appends whitespace

I have the following directory structure:
/home/tichy/xxx/yyy/aaa
/home/tichy/xxx/yyy/aab
/home/tichy/xxx/yyy/aac
I would like to enter cdw y<TAB> and get cdw yyy/<CURSOR> as a result, so I could add cdw yyy/a<TAB> and get cdw yyy/aa<CURSOR>
The solution I came up with gives me the following:
cdw y<TAB> => cdw yyy<SPACE><CURSOR>
Following code I have so far:
_cdw () {
local cur prev dirs
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=($(compgen -d -- /home/tichy/xxx/${cur}|perl -pe 's{^/home/tichy/xxx/}{}'))
# no difference, a bit more logical:
dirs=$(compgen -o nospace -d /home/tichy/xxx/${cur}|perl -pe 's/{^/home/tichy/xxx/}{}')
COMPREPLY=($(compgen -d -W ${dir} ${cur}|perl -pe 's{^/home/tichy/xxx/}{}'))
return 0
}
complete -F _cdw cdw
cdw () {
cd /home/tichy/xxx/$#
}
Any ideas what's wrong? It seems to me that the completion process seems to be finished and isn't expecting any more input.
The simplest solution I've found so far is to generate completions that look like this:
COMPREPLY=( $( compgen -W "file1 file2 file3 dir1/ dir2/ dir3/" ) )
and add this line just before returning
[[ $COMPREPLY == */ ]] && compopt -o nospace
This sets the nospace option whenever the completion may fill in until the slash so that the user ends up with:
cmd dir1/<cursorhere>
instead of:
cmd dir1/ <cursorhere>
and it doesn't set the nospace option whenever the completion may fill in until a full filename so that the user ends up with:
cmd file1 <cursorhere>
instead of:
cmd file1<cursorhere>
If I understand correctly, you want to bash-autocomplete a directory name, and not have the extra space? (That's what I was looking for when I got to this page).
If so, when you register the completion function, use "-o nospace".
complete -o nospace -F _cdw cdw
I don't know if nospace works on compgen.
How about something like this:
COMPREPLY=( $(cdw; compgen -W "$(for d in ${cur}* ${cur}*/*; do [[ -d "$d" ]] && echo $d/; done)" -- ${cur}) )
(I'm not sure if you can call your shell function from here or not, otherwise you may have to duplicate it a bit.)
This also gets rid of your perl hack :-)
completion provides a solution for this without any workaround: funciton _filedir defined in /etc/bash_completion:
626 # This function performs file and directory completion. It's better than
627 # simply using 'compgen -f', because it honours spaces in filenames.
628 # #param $1 If `-d', complete only on directories. Otherwise filter/pick only
629 # completions with `.$1' and the uppercase version of it as file
630 # extension.
631 #
632 _filedir()
Then, specifying the following is enough:
_cdw () {
local cur prev dirs
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
_filedir # add -d at the end to complete only dirs, no files
return 0
}

Resources