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 dictionary lookup program) that takes various words as arguments. The supported ‘words’ may actually contain spaces, and are defined in a file called words.dat
:
foo
bar one
bar two
Here’s my first suggested solution:
_find_words()
{
search="$cur"
grep -- "^$search" words.dat
}
_words_complete()
{
local IFS=$'\n'
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=( $( compgen -W "$(_find_words)" -- "$cur" ) )
}
complete -F _words_complete words
Typing ‘words f<tab>’
correctly completes the command to ‘words foo ’
(with a trailing space), which is nice, but for ‘words b<tab>’
it suggests ‘words bar ’
. The correct completion would be ‘words bar\ ’
. And for ‘words "b<tab>’
and ‘words 'b<tab>’
it offers no suggestions.
This last part I have been able to solve. It’s possible to use eval
to properly parse the (escaped) characters. However, eval
is not fond of missing quotes, so to get everything to work, I had to change the search="$cur"
to
search=$(eval echo "$cur" 2>/dev/null ||
eval echo "$cur'" 2>/dev/null ||
eval echo "$cur\"" 2>/dev/null || "")
This actually works. Both ‘words "b<tab>’
and ‘words 'b<tab>’
correctly autocompletes, and if I add a ‘o’
and press <tab>
again, it actually completes the word and adds the correct closing quote. However, if I try to complete ‘words b<tab>’
or even ‘words bar\ <tab>’
, it is autocompleted to ‘words bar ’
instead of ‘words bar\ ’
, and adding for instance ‘one’
would fail when the words
program is run.
Now, obviously it is possible to handle this correctly. For instance, the ls
command can do it for files namned ‘foo’
‘bar one’
and ‘bar two’
(though it does have problems with some ways of expressing the filenames when one uses a (valid) combination of both "
, '
and various escapes). However, I couldn’t figure out how ls
does it by reading the bash completion code.
So, does anybody know of how properly handle this? The actual input quotes need not be preserved; I would be happy with a solution that changes ‘words "b<tab>’
, ‘words 'b<tab>’
and ‘words b<tab>’
to ‘words bar\ ’
, for instance, (though I would prefer stripping of quotes, like in this example, instead of adding them).