Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have years of experience on Slackware linux, and have never seen this problem there. Lately I have been administering Debian servers, and this has become an annoyance.

I frequently use "$_" in a bash command to repeat the final argument of the previously-typed command. Sometimes, I get the following behavior:

dschrag@mo:~/dev/dancer$ ls -l app/db/schema/
total 12
-rw-r--r-- 1 dschrag dschrag 268 May 19 10:51 gau_account.sql
-rw-r--r-- 1 dschrag dschrag 558 May 19 13:02 gau_import.sql
-rw-r--r-- 1 dschrag dschrag 270 May 19 13:09 gau_source.sql
dschrag@mo:~/dev/dancer$ cd $_
-bash: cd: _filedir: No such file or directory
dschrag@mo:~/dev/dancer$

I've looked around enough to see that _filedir is used in many pre-defined (by default) shell functions defined in /usr/share/bash-completion/bash_completion.

I use file-completion without thinking, to the point it's hard for me to figure out the exact thing I am doing when this glitch occurs. Has anyone taken the trouble to dig deeper into this?

share|improve this question

2 Answers 2

up vote 1 down vote accepted

A simple tracing

First, you can figure out that what actually happen when you using bash completion with cd command:

$ complete -p | grep cd
complete -F _filedir_xspec cdiff
complete -o nospace -F _cdrecord wodim
complete -o nospace -F _cdrecord cdrecord
complete -o nospace -F _cd cd

You can see a function _cd will be called when you use completion with cd. So you know that some thing happened inside _cd function causes your $_ variable value had been altered.

Looking into _cd function: (In my Ubuntu 12.04.4, it's in /etc/bash_completion)

# This meta-cd function observes the CDPATH variable, so that cd additionally
# completes on directories under those specified in CDPATH.
#
_cd()
{
    local cur IFS=$'\n' i j k
    _get_comp_words_by_ref cur

    # try to allow variable completion
    if [[ "$cur" == ?(\\)\$* ]]; then
        COMPREPLY=( $( compgen -v -P '$' -- "${cur#?(\\)$}" ) )
        return 0
    fi
    ....
        _filedir -d

    if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
        i=${COMPREPLY[0]}
        if [[ "$i" == "$cur" && $i != "*/" ]]; then
            COMPREPLY[0]="${i}/"
        fi
    fi

    return 0
}

It does many things, causes your old $_ lost. Maybe you have trouble with line _filedir -d. (In my test, I got $_ changed to -d)

To preventing your wanted $_ from losing, you can save its value immediately when you enter _cd function, then restore this value to $_ before leaving _cd:

_cd()
{
    local __=$_

    ....

    : $__
    return 0
}

The trick here is :, it means do nothing in bash, and it has one parameter $__ (which is your wanted $_ value), causing $_ is set to old value before you enter _cd function.

Conclusion

It's very difficult to make bash completion does not trashing $_ variable. With above example, you only prevented it from trashing with cd. With others function in bash_completion and all files under /etc/bash_completion.d/, you have to do the same thing. This is not a robust solution.

If you want to get the last parameter of the last command line, try using bash history expansion instead:

$ echo 1 2 3
1 2 3
$ echo !$
echo 3
3
share|improve this answer

You've got programmable completion ("bash completion") enabled. That's done by sourcing /etc/bash_completion or /usr/share/bash-completion/bash_completion somewhere in your bashrc. For example, in the skeleton bashrc on jessie:

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

One of the completions is overwriting $_. The quick workaround would be to not load bash-completion.

Another workaround is to do your filename completions with the complete-filename function instead of the complete function. That'd mean pressing ESC, / or Alt+/ (depending on your terminal setup) instead of Tab. You can change which keys those are bound to with bind or by editing ~/.inputrc.

And, I suppose, file a bug report against bash-completion.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.