Bash-Completion, one list shown, the other not.

Heyas

I'm trying to read out a file which contains a variable and use that list to complete a bash command.
Difficult part on this is, that 2 (actualy 3) different lists are to be used, but the 'dynamic' ones from the external file dont work properly.

It only seems to work with the list which is hardcoded into the bash_completion file, but not with variables from outside, though a simple echo at the wrong place, shows the value is read... :confused:

list1="a123 a456 a789"
list2="abc def ghi"

Then, for the actual bash completion, i'd expect this:

XY -c a${list[1} 
XY -c b${list[2}

But what i get is this:

+ files $ ll [fr]* ; set -x ; . repro* ;  cat file.list ; set +x
-rw-rw-r--. 1 sea sea   61 10. Apr 19:14 file.list
-rw-rw-r--. 1 sea sea 1.7K 10. Apr 19:49 reproducable.bash
+ . reproducable.bash
++ complete -F _XY_module XY
++ complete -F _XY_module XY.sh
+ cat file.list
section_task3="babc bdef bghi bjkg blmn bopq brst buvw bxyz"
+ set +x

:) files $ ll [fr]* ; set -x ; . repro*XY -c 
a  b  

:) files $ XY -c a
a1024  a128   a192   a256   a384   a512   a768   a96 
   
:) files $ XY -c b 
a  b  

:) files $ XY -c b b 
a  b  

:) files $ XY -c b b b 
a  b  

:) files $ XY -c b b b a
a1024  a128   a192   a256   a384   a512   a768   a96

:) files $ XY -c b b b a1
a1024  a128   a192   

This is a short version of the full bash_completition script, and beeing reproducable:

_XY_module()
{
#
#	Variables
#
	local cur prev OPTS
	COMPREPLY=()
	OPTS="-a -b"
	cur="${COMP_WORDS[COMP_CWORD]}"
	prev="${COMP_WORDS[COMP_CWORD-1]}"
	
	[ -f file.list ] || printf "%s\n" "section_task2=\"abc def ghi jkg lmn opq rst uvw xyz\"" > file.list
	section_task1="a96 a128 a192 a256 a384 a512 a768 a1024"
	section_task2="b$(grep section_task2 file.list | sed s,section_task2=,, | sed s,\ ,\nb,)"
#
#	Action
#
	# This shows a list of words applying to your last argument
	# These need manual maintainance
	case "$prev" in
	-c)
		case $cur in
		a*)		COMPREPLY=( $( compgen -W "$(echo $section_task1|grep $cur*)" -- "$cur" ) ) 
				return 0
				;;
		b*)		COMPREPLY=( $( compgen -W "$(echo $section_task2|grep $cur*)" -- "$cur" ) ) 
				return 0
				;;
		"")		COMPREPLY=( $( compgen -W "a b" -- $cur) )
				return 0
				;;
		esac
		;;
	XY|XY.sh)
		COMPREPLY=( $(compgen -f -- $cur) )
		return 0
		;;
	esac
	# This completes the word you are currently writing
	# These need manual maintainance
	case $cur in
	-*)	COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
		return 0
		;;
	a*)	COMPREPLY=( $( compgen -W "$(echo $section_task1|grep $cur*)" -- "$cur" ) ) 
		return 0
		;;
	b*)	COMPREPLY=( $( compgen -W "$(echo $section_task2|grep $cur*)" -- "$cur" ) ) 
		return 0
		;;
	*)	COMPREPLY=( $( compgen -W "a b" -- $cur) )
		return 0
		;;
	esac
	
}
# Actualy make it available to the shell
# Cover with and without extension
complete -F _XY_module XY
complete -F _XY_module XY.sh

Any advice or hints please?
Thank you in advance and have a nice weekend :slight_smile:

It is hard to comment when we don't know what the complete or compgen commands look like and we don't know how COMP_CWORD and the COMP_CWORDS array are set when you enter _XY_module() . But, a few things look strange:

You initialize OPTS to a string:

OPTS="-a -b"

but you refer to it as an array:

COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )

Decide whether it is an array or a string and use it consistently in your script.

You call: compgen -W "${OPTS[*]}" -- $cur
which will expand to: compgen -W "-a -b" -- $cur
You also call: compgen -W "a b" -- $cur
Does the compgen -W option expect an option-argument both with and without hyphens?

If the file file.list does not exist, you create that file and put a definition for the variable section_task2 in it. In the trace output you showed us, the file does exist and only contains a definition for the variable section_task3 .

Two lines later in your script you set the variable section_task2 to the string to which section_task2 had been defined in that file with a leading b added and the 1st (and only the 1st) space changed to a <newline> character or to the letter n (depending on what operating system and version of sed you're using) followed by the letter b . I would have guessed that if you were going to change any spaces in that string, you would want to change them all. But, since there is no section_task2 defined in that file, section_task2 in your script is defined to just be the string b .

In the trace log and in the code you use if the file doesn't exist, you double quote the string that is placed in the file as the value of section_task2 or section_task3 . Your sed script doesn't remove those quotes!

And, why are you using two sed commands, when you could use a single sed command with two substitute commands? (Of course, you could also skip the grep command, if your sed script would delete lines that don't start with the variable you're interested in before processing the substitute commands.) Assuming that the variable you're looking for in the file is section_task2 and not section_task3 , maybe you would want something like:

section_task2=$(sed -e '/^section_task2=/!d' -e 's/^[^=]*="//' -e 's/"$//' -e 's/^/b/' -e 's/ /\
b/g' file.list)

Hi Don, the creation of the missing file.list and the naming of the variable section_task3 was just for this sharing, as that file.list was created by the application, not by the bash completion.
Should have placed it outside the function, sorry.
Naming it 3 was a typo, should have been 2, which was corrected while you wrote.

The OPTS (-a -b) are just used as metapher for all the options there are, but its right, should have shorten it to '-c' only, for easier seperation.
Anyhow, the issue is, i want to morph the list of that file (abc def ghi ...) so that each entry of that list starts with letter 'b', as in babc, bdef bghi.

To be honest, i barley understood any of your question that were bash-completion specific, i just copied and modified the file, and played around trying to match/fit my needs.
As:

Saying: I 'know' it works the way it is, but i dont know why or how exactly...
I thought prev="${COMP_WORDS[COMP_CWORD-1]}" is an integrated expression, similar to vars="${@[${#@}-1]}"

So i've adapted your suggestion, and, at least its not using 'b' as a single letter anymore.

+ files $ source ./reproducable.bash 
:) files $ XY -c a
a1024  a128   a192   a256   a384   a512   a768   a96    
:) files $ XY -c b

When i try to print the content of section_task2, i get this:

^C
130 files $ XY -c 


a  b  
130 files $ XY -c a


a1024  a128   a192   a256   a384   a512   a768   a96    
130 files $ XY -c b






bash: XY: Command not found.
127 files $ 

With this code on top:

	[ -f file.list ] || printf "%s\n" "section_task2=\"abc def ghi jkg lmn opq rst uvw xyz\"" > file.list
	section_task1="a96 a128 a192 a256 a384 a512 a768 a1024"
	#section_task2="b$(grep section_task2 file.list | sed s,section_task2=,, | sed s,\ ,\nb,)"
	section_task2=$(sed -e '/^section_task2=/!d' -e 's/^[^=]*="//' -e 's/"$//' -e 's/^/b/' -e 's/ /\b/g' file.list)
	set +x
	echo $section_task2 ##; return

Seems section_task2 is empty?

A final word about the use of array while its declared (set) as variable, have a look at the git bash completion, i copied from there.
I'm not saying its a temporary solution, but its working as is. -> never change a running system you dont fully understand :wink:
Yet i have/want to change this behaviour for better user support.

Thank you