1

When using tab completion in bash, the $_ variable is altered:

$ mkdir test
$ cd <TAB><TAB> $_
bash: cd: -d: invalid option
cd: usage: cd [-L|[-P [-e]]] [dir]

(The <TAB><TAB> will list all files in current directory, but I don't end up using the output and write $_ instead. The command executed in this line will just be cd $_.)

Expected behavior would be to change into ./test.

How can I prevent bash completion from altering $_?

kurtm
7,4051 gold badge26 silver badges22 bronze badges
asked Oct 12, 2013 at 8:47
4
  • 3
    bash is pretty limited with its autocompletion options: I am not sure if that is possible. If you need more powerfull and customisable autocompletion in interactive session I suggest moving to zsh if that is possible. Commented Oct 12, 2013 at 9:10
  • 4
    This is almost certainly due to programmable completion, the default (readline) completion doesn't do this. Please indicate which version of bash, which completion package and/or the output of complete -p. Commented Oct 12, 2013 at 9:55
  • cd <TAB><TAB> should give you all possible completions (the 2nd tab),how are you entering $_? Commented Oct 12, 2013 at 14:03
  • @mr.spuratic Bash version is GNU bash is version 4.2.45(2)-release on x86_64. The output of complete -p is here: js.f0i.de/complete-p.html Commented Oct 13, 2013 at 14:53

1 Answer 1

2

You're using the bash-completion package (or a derivative). For each argument completion of the cd command (as shown by the complete -p output):

complete -o nospace -F _cd cd

the _cd function is invoked to determine the completions (slightly edited for brevity):

_cd()
{
 local cur prev words cword
 _init_completion || return
 local IFS=$'\n' i j k
 compopt -o filenames
 if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then
 _filedir -d
 return 0
 fi
 ....

So for example, when you complete on a directory with no CDPATH set, the last-seen argument to a command seen is -d, and this is placed automatically in _. There are several other code paths in that function with similar side-effects.

Since _ is a bash internal, a conventional save/restore (as for IFS) won't work as hoped. You could do it with a little trickery:

_cd()
{
 local save_="$_"
 ...
 : $save_
 return 0

You must save _ immediately on entry to a function, : is the null command, which does nothing per-se but has the usual side-effects of a command, such as setting _. This restore operation will be required for each return point of each potentially disruptive function. There's a subtlety here too: normally _ is set immediately after a return from a function (to the last argument of the function call, as expected), which would make this method ineffective. But, this doesn't happen when a completion function is invoked, since it's not explicit invoked. I don't consider this to be very robust...

(I prefer history expansion, and stick to !$ which doesn't suffer this problem.)

answered Oct 13, 2013 at 20:41

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.