Tuesday, February 6, 2024

[SOLVED] Bash Autocompletion - How to pass this array to compgen without significant whitespace being collapsed?

Issue

The following bash completion passes an array of possible words (i.e. completions) to compgen.

basenames=("foo" "fu bar" "baz");

COMPREPLY=($(compgen -W "${basenames[*]}" -- "${COMP_WORDS[COMP_CWORD]}"))

The problem is that the whitespace in the array elements is not preserved, that is "foo bar" is treated as to elements thanks to word splitting. Is there some way to preserve the whitespace, so that 3 elements are shown, and not 4?

EDIT

basenames contains filenames, that is nearly every character (apart from / and \0) is allowed.

EDIT 2

The -W flag expects a single word, that is something like foo bar foobar. Passing multiple elements to it (this is what ${basenames[@]} would do) won't work.

EDIT 3

Changed examplary basenames array (so the foo and the foo from foo bar won't get collapsed).

Using a newline to separate the words works:

local IFS=$'\n'
COMPREPLY=($(compgen -W "$(printf "%s\n" "${basenames[@]}")" --  ${COMP_WORDS[COMP_CWORD]}"))

Using \0 doesn't:

local IFS=$'\0'
COMPREPLY=($(compgen -W "$(printf "%s\0" "${basenames[@]}")" --  ${COMP_WORDS[COMP_CWORD]}"))

Solution

Why bother with compgen? Just add them to COMPREPLY manually. The following will complete the matching filenames from /some/path, handling filenames safely.

some_completion_function() {
    local files=("/some/path/$2"*)
    [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]##*/}" )
}

It's not possible to have compgen handle filenames safely.



Answered By - geirha
Answer Checked By - Candace Johnson (WPSolving Volunteer)