You are not logged in.
So I'm trying to wrap my head around writing a bash completion function for my personal use and have struggled through to get something that kinda almost works.
This should be fairly simple, I have a bash function that I use to remove package(s) from a local aur utils repo.
$ type rr
rr is a function
rr ()
{
repo-remove /home/ghost/.aur/aur.db.tar "$@"
}
What I'd like to do is be able to auto-complete so that it parses all packages in the local repo and returns it as a list to complete through. I then thought, that I'd prefer it only return a list of packages that are not currently installed, and finally I decided why not default to non-installed packages but allow a flag to show all.
Okay, so here's the script:
_rr(){
COMPREPLY=()
local cur prev
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
mapfile -t FILTERED_PKGS < <(pacman -Sl aur | awk '!/installed/ {print $2}')
mapfile -t ALL_PKGS < <(pacman -Slq aur)
#echo ""
#echo "COMP_WORDS : ${COMP_WORDS} is array"
#echo "COMP_WORDS WHOLE ARRAY : ${COMP_WORDS[@]} are the array elements"
#echo "COMP_CWORD : ${COMP_CWORD} is the current word index in the array"
#echo "COMP_WORDS[COMP_CWORD] : ${COMP_WORDS[COMP_CWORD]} is the current word"
#echo "COMP_LINE : ${COMP_LINE} is the entire array accessed differently"
#echo "COMP_POINT : ${COMP_POINT} is the current character index of array"
#echo "COMP_KEY : ${COMP_KEY}"
#echo "COMP_TYPE : ${COMP_TYPE}"
#echo "args : $@"
#echo "reply : ${COMPREPLY}"
#echo "cur is : $cur"
#echo "prev is : $prev"
#echo ""
if [ "$prev" = "-a" ] ; then
COMPREPLY=($(compgen -W "${ALL_PKGS[*]}" "$cur"))
else
COMPREPLY=($(compgen -W "${FILTERED_PKGS[*]}" "$cur"))
fi
return 0
}&&
complete -F _rr rr
This works okay, but here's what I'm missing:
1. Once an item from the complete list selected, how do I then remove the option from the next list of completion words that is presented? Lets say I have three completion options "foo1 foo2 foo3" and I auto-complete to select foo1 .."rr foo1 [tab]" should only return "foo2 foo3", right now it will return all three again allowing multiple selections of the same item.
2. Assuming that the option "-a" should only be the first option passed, how can I check the "-a" as always the first argument to the "rr" command?
"the wind-blown way, wanna win? don't play"
Offline
You could just check for equality and set another variable to determine whether the first argument is -a.
This does that:
#!/bin/bash
DOALL=0
if [[ "$1" == "-a" ]]
then
echo "\$1 == -a"
DOALL=1
else
echo "\$1 == -a returned false"
fi
if [[ "$DOALL" == 1 ]]
then
echo "DOALL was set to 1"
else
echo "DOALL is zero"
fi
To get the list options to be dynamic you can write the output of options to a file, then parse the file and print the options from there. Then you could remove the line from the file so next time that option is gone.
Here's a script I've used to read arguments from a file line-by-line loop, where I used each line as an argument in running another program:
#!/bin/bash
INPUT=./combos
HFUN=$1
IFS=\n
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 0; }
echo "start - heuristic number $HFUN: $(date +%Y-%m-%d-%H:%M:%S)" >> testtime$HFUN
while read line
do
./astar h$HFUN "$line" testing
done < $INPUT | sort -n -r -k 1,1 >> testtime$HFUN
echo "end - heuristic number $HFUN: $(date +%Y-%m-%d-%H:%M:%S)" >> testtime$HFUN
I'm no pro at bash scripts, I just found this page which looks like a good resource: https://tldp.org/LDP/abs/html/
Looks like you can also read the options into an array, unset indices as they are used, and when you gather a list for printing you can loop for each index in the array https://linuxconfig.org/how-to-use-arra … ash-script
Last edited by chaseleif (2020-08-30 07:01:25)
Offline
@chaseleif thanks!
I was trying to use the $1 $2 $3 etc variables within the custom complete function, but it appears that those have a special/different meaning than in regular bash shell? AFAICT anyway. It seems that instead of referring to static list of arguments passed to a command, they reference specific words in the completion chain, some which are dynamic and change based on what's being completed and has been completed.
(btw this blog post quoted below is a really good resource on custom completions as well).
$1 : the first argument is the name of the command whose arguments are being completed
$2 : the second argument is the word being completed
$3 : the word preceding the word being completed on the current command line
So in this case $1 is always 'rr' and $2 is basically $cur and $3 is basically $prev? (i.e. constantly updates based on what's being completed or has been completed). I'm not sure of the customary way to handle this so I just went for using the $COMP_WORDS[1] index instead - still using your suggested method.
_rr(){
COMPREPLY=()
local cur prev DOALL
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
mapfile -t FILTERED_PKGS < <(pacman -Sl aur | awk '!/installed/ {print $2}')
mapfile -t ALL_PKGS < <(pacman -Slq aur)
if [[ ${COMP_WORDS[1]} && ${COMP_WORDS[1]} = "-a" ]]; then
COMPREPLY=($(compgen -W "${ALL_PKGS[*]}" "$cur"))
else
COMPREPLY=($(compgen -W "${FILTERED_PKGS[*]}" "$cur"))
fi
return 0
}&&
complete -F _rr rr
I've played around with your suggestions for removing already chosen completions. I think I'd like to try and use an associative array to do this rather than an external file. Something like the following I think? (Is there a better way to map this out than two for loops?)
...
mapfile -t FILTERED_PKGS < <(pacman -Sl aur | awk '!/installed/ {print $2}')
mapfile -t ALL_PKGS < <(pacman -Slq aur)
declare -A ALL_HOLDING FILTERED_HOLDING
for ((i = 0; i < "${#FILTERED_PKGS[@]}"; i++)); do
FILTERED_HOLDING["${FILTERED_PKGS[$i]}"]=${FILTERED_PKGS[$i]}
done
for ((i = 0; i < "${#ALL_PKGS[@]}"; i++)); do
ALL_HOLDING["${ALL_PKGS[$i]}"]="${ALL_PKGS[$i]}"
done
...
but now I'm not sure of the best way to remove the completion from either array.
?I guess I should only remove the previous completion (ie. $3) from either holding array as long as it is not a blank?... I'll have to think/google about this some more.
In the meantime though I have satisfactory auto-completion working. Thanks for your insight.
Last edited by CarbonChauvinist (2020-08-31 00:34:38)
"the wind-blown way, wanna win? don't play"
Offline
Cool stuff.
So, looks like $1 to $9 are ordered arguments, from "How to Use Command Line Parameters in Scripts" down on this page: https://www.howtogeek.com/442332/how-to … s-in-bash/
or this page looks like it may have even more stuff: https://www.baeldung.com/linux/bash-special-variables
That $cur/$prev looks like just their parsing scheme, I think
The for loops can have different limits and performance isn't really a concern, I wouldn't try to combine them.
The last link about arrays I posted last time shows how to delete specific elements and iterate over valid elements:
https://linuxconfig.org/how-to-use-arra … -the-array
(This bookmark brings you toward the bottom to the unset part of the page)
For deleting an element, right above that you see a for loop for [element] in array, where it skips an index that has been deleted.
Offline