Bash Autocomplete – Why Does nullglob Affect Tab Completion?

autocompletebash

After shopt -s nullglob, I noticed that tab completion completely stopped working. Why would that be? extglob is apparently benign, what else could nullglob affect?

Observed on:

  • Ubuntu 14.04 (bash 4.3.11(1)-release)
  • Arch Linux (bash 4.3.42(1)-release)

Best Answer

Unlike extglob, nullglob makes a huge difference in the shell behavior. It means that a word containing wildcard characters that are likely to match nothing result in a word disappearing instead of being kept.

In most cases, code that breaks with nullglob would also break without it, if a certain directory contains files that happen to match the unquoted pattern. Depending on the pattern, the existence of such files may be more or less likely (and it may be impossible if the scripts controls what file names are present, e.g. because it switches to a temporary directory that it populates).

Bash's tab completion is not affected by nullglob by default, but it is affected if you enable programmable completion, because some of the bash code that implements programmable completion is not robust.

Looking at what happens when completing the argument of ls with set -x turned on, I see that the output with and without nullglob starts differing at

+ [[ 0 -gt 0 ]]
+ ref='words[0]'
+ eval 'words[0]=${!ref}${COMP_WORDS[i]}'
++ words[0]=ls
+ line=' '

(working) vs.

+ [[ 0 -gt 0 ]]
+ ref='words[0]'
+ eval
+ line=' '

with nullglob. See that eval line? That's a sign that the argument looked like a glob pattern. The corresponding code is in the __reassemble_comp_words_by_ref function:

                # Append word separator to current or new word
                ref="$2[$j]"
                eval $2[$j]=\${!ref}\${COMP_WORDS[i]}

[ is a wildcard, so $2[$j]=\${!ref}\${COMP_WORDS[i]} is a wildcard pattern, and with nullglob, it's eliminated since it matches nothing. This would also break even without nullglob if the current directory contained a file called words0=${!ref}${COMP_WORDSi} — that's pretty exotic, but it could happen.

The fix is to add the missing double quotes:

                eval "$2[$j]=\${!ref}\${COMP_WORDS[i]}"

There are probably other parts of that script that need similar fixes, I haven't investigated further.

This is a bug in bash_completion (not in bash itself). It was reported in 2012 and fixing it is on the roadmap for version 3.0.