2024-04-09
Bash's sadly flawed smart (programmable) completion
Bash has an interesting and broadly useful feature called 'programmable completion' (this has sort of come up before). Programmable completion makes it possible for Bash to auto-complete things like command line options for the current program for you. Unfortunately one flaw in Bash's programmable completion is that it doesn't understand enough Bash command line syntax and so can get in your way.
Suppose, not hypothetically, that you are typing the following on the Bash command line on a Debian-based Linux system, with the bits in bold being what you typed before you hit tab:
# apt-get install $(grep -v '^#' somefi
<TAB>
When you hit TAB to complete the file name, nothing will happen.
This is because Bash has been told what the arguments to apt-get
are and so 'knows' that they don't include files (this is actually
wrong these days, but never mind). Bash isn't smart enough to
recognize that by typing '$(
' you've started writing a command
substitution and are now in a completely different completion
context, that of grep
, where you definitely should be allowed to
complete file names.
Bash could in theory be this smart but there are probably a number
of obstacles to doing that in practice. For example, we don't have
a well-formed command substitution here, since we haven't typed the
closing ')
' yet; Bash would effectively have to do something like
the sort of on the fly parsing of incomplete code that editor
autocompletion does. It's possible that Bash could do better with
some heuristics, but the current situation is broadly easy to explain
and reason about, even if the result is sometimes frustrating.
There are at least two ways to disable these programmable completions
in Bash. You can turn off the feature entirely with 'shopt -u
progcomp
' or you can flush all of the registered programmable
completions with 'complete -r
'. In theory you can see the list
of current completions with 'complete -p
' and then remove just
one of them with 'complete -r <name>
', but in practice 'complete
-p' doesn't always list a program until I've started trying to do
completions with it.
(Where the shell snippets that define completions go is system
dependent, but Linux systems will often put them in
'/usr/share/bash-completion/completions
'.)
People with better memories than me can also use M-/ instead to force completion of a filename no matter what Bash's programmable completion thinks should go there. M-! will complete command names, M-$ will complete variable names, and M-~ will complete user names. You can find these in the Bash manual page as the various 'complete-*' readline (key binding) command names.
(Another general flaw on programmable completion is that it relies on the people who provide the completion definitions for commands getting it right and they don't always do that, as I've seen in the past.)