Bash Tab-autocompletion inside command substitution "$( ... )" [duplicate]
I'm trying to go into an irb
session with specific environment variables from a file with this command:
$ env $(cat env.sh) irb
But when I try press Tab
after I type env.
to complete it, I get this following error:
$ env $(cat env.-bash: unexpected EOF while looking for matching `)'
-bash: syntax error: unexpected end of file
Another interesting thing is that if I'm logged in as root, this error does not occur.
Here's the output of find ~ -uid 0
:
$ find ~ -uid 0
/home/(redacted)/.rpmdb
/home/(redacted)/.rpmdb/Group
/home/(redacted)/.rpmdb/Conflictname
/home/(redacted)/.rpmdb/Installtid
/home/(redacted)/.rpmdb/Sha1header
/home/(redacted)/.rpmdb/Providename
/home/(redacted)/.rpmdb/__db.002
/home/(redacted)/.rpmdb/Requirename
/home/(redacted)/.rpmdb/Sigmd5
/home/(redacted)/.rpmdb/__db.001
/home/(redacted)/.rpmdb/Obsoletename
/home/(redacted)/.rpmdb/.dbenv.lock
/home/(redacted)/.rpmdb/Name
/home/(redacted)/.rpmdb/Basenames
/home/(redacted)/.rpmdb/Triggername
/home/(redacted)/.rpmdb/Packages
/home/(redacted)/.rpmdb/Dirnames
/home/(redacted)/.rpmdb/__db.003
Can anyone explain to me why this is happening and if so, how do I fix it in when I'm not a root user?
Solution 1:
You found a bug in the Bash Completion library used by Ubuntu.
What does this mean?
Ubuntu uses a bash completion library to make bash completion smart. This library lives in /usr/share/bash-completion/bash_completion
.
Essentially, this library declares a few clever functions that know about typical commands and how to complete them. Whenever you press Tab, functions within this library get called and attempt to complete your current command line. So for example if you type apt-get i
Tab it will complete that to apt-get install
. If you don't source that library, you only have the standard, primitive bash completion - so for example if you type apt-get i
Tab without having sourced it, bash will simply look for files in the current directory starting with i
and attempt to complete your command according to these filenames.
Why isn't it happening as root?
Because when you use sudo su
to make yourself root
, the bash completion library isn't sourced. This would be different if you used sudo -i
to make yourself root
. I bet you see the bug then, don't you? See for example 'sudo su -' vs 'sudo -i' vs 'sudo /bin/bash' - when does it matter which is used, or does it matter at all? if you aren't familiar with the differences.
In my case, as a normal user, the library gets sourced when I start a Bash shell because ~/.bashrc
sources /etc/bash_completion
which sources /usr/share/bash-completion/bash_completion
.
If I use sudo -i
to login as root
, the library gets sourced because /etc/profile
sources /etc/profile.d/bash_completion.sh
which sources /usr/share/bash-completion/bash_completion
.
Why is that bug happening?
Try to execute this command:
$ eval 'quoted=$(cat' env.
bash: unexpected EOF while looking for matching `)'
bash: syntax error: unexpected end of file
Looks familiar? ;-) Indeed, that's exactly what happened behind the scenes when you hit Tab in the context you described. More precisely, the bug is in the function _quote_readline_by_ref
declared by /usr/share/bash-completion/bash_completion
. If you sourced that file you should have that function available. So next try this:
$ _quote_readline_by_ref '$(cat env.' quoted
bash: unexpected EOF while looking for matching `)'
bash: syntax error: unexpected end of file
Given these arguments, the function _quote_readline_by_ref
performs, among other things, that eval
mentioned above. You can have a look if you like. And when you typed env $(cat env.
Tab, behind the scenes that function got called with exactly those arguments. So that's what happened.
This eval
hack was supposed to fix another issue, but I guess it introduced this other bug in the process.
How do I fix it?
It turns out that this bug has already been reported. After reading that bug report, I see three ways to fix it:
-
Patch it: In one of the comments in that bug report, someone suggests replacing the line
[[ ${!2} == \$* ]] && eval $2=${!2}
within function
_quote_readline_by_ref
in the file/usr/share/bash-completion/bash_completion
by the line[[ ${!2} == \$\'* ]] && eval $2=${!2}
I recommend against doing this. The person who wrote that comment does not appear to be a developer of bash-completion. This hotfix will simply cause the left operand of the statement to evaluate to false and thus prevent that
eval
from happening. However without a good knowledge of what that function is supposed to do and in what contexts it is called, it is unclear whether this will not potentially break some other intended functionality. -
Get the newest version: As also mentioned in that bug report, this bug is not present in git head (wherein among other changes the function
_quote_readline_by_ref
has been simplified). You can simply clone the current revision from Git:git clone https://salsa.debian.org/debian/bash-completion.git
...and then copy the newest version of the
bash_completion
script to/usr/share/bash-completion
(no urgent need to backup the old version unless it makes you feel safer - if you experience some problems,sudo apt-get install --reinstall bash-completion
should revert any changes you made just fine.) This is the way I recommend if you're in a hurry to get this fixed. :-)
Note that none of those solutions will make bash completion inside command substitution work: as mentioned in that same bug report, this is broken in Bash 4.3.
- Sit back and wait: Sooner or later a new version will get released (which may even fix bash completion inside command substitution) and you will get it with some future Ubuntu version. That's what I'm going for ;-)