What is the correct/best way of handling spaces and quotes in bash completion?
Here’s a simple example. I have a command called words (e.g., a dictionar
This not too elegant postprocessing solution seems to work for me (GNU bash, version 3.1.17(6)-release (i686-pc-cygwin)). (Unless I didn't test some border case as usual :))
Don't need to eval things, there are only 2 kinds of quotes.
Since compgen doesn't want to escape spaces for us, we will escape them ourselves (only if word didn't start with a quote). This has a side effect of full list (on double tab) having escaped values as well. Not sure if that's good or not, since ls doesn't do it...
EDIT: Fixed to handle single and double qoutes inside the words. Essentially we have to pass 3 unescapings :). First for grep, second for compgen, and last for words command itself when autocompletion is done.
_find_words()
{
search=$(eval echo "$cur" 2>/dev/null || eval echo "$cur'" 2>/dev/null || eval echo "$cur\"" 2>/dev/null || "")
grep -- "^$search" words.dat | sed -e "{" -e 's#\\#\\\\#g' -e "s#'#\\\'#g" -e 's#"#\\\"#g' -e "}"
}
_words_complete()
{
local IFS=$'\n'
COMPREPLY=()
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=( $( compgen -W "$(_find_words)" -- "$cur" ) )
local escaped_single_qoute="'\''"
local i=0
for entry in ${COMPREPLY[*]}
do
if [[ "${cur:0:1}" == "'" ]]
then
# started with single quote, escaping only other single quotes
# [']bla'bla"bla\bla bla --> [']bla'\''bla"bla\bla bla
COMPREPLY[$i]="${entry//\'/${escaped_single_qoute}}"
elif [[ "${cur:0:1}" == "\"" ]]
then
# started with double quote, escaping all double quotes and all backslashes
# ["]bla'bla"bla\bla bla --> ["]bla'bla\"bla\\bla bla
entry="${entry//\\/\\\\}"
COMPREPLY[$i]="${entry//\"/\\\"}"
else
# no quotes in front, escaping _everything_
# [ ]bla'bla"bla\bla bla --> [ ]bla\'bla\"bla\\bla\ bla
entry="${entry//\\/\\\\}"
entry="${entry//\'/\'}"
entry="${entry//\"/\\\"}"
COMPREPLY[$i]="${entry// /\\ }"
fi
(( i++ ))
done
}