Understand the meaning of `$_`

I agree it's not very clear.

1. At shell startup,

  • if the _ variable was in the environment that bash received, then bash leaves it untouched.

    In particular, if that bash shell was invoked by another bash shell (though zsh, yash and some ksh implementations also do it), then that bash shell will have set the _ environment variable to the path of the command being executed (that's the 3rd point in your question). For instance, if bash is invoked to interpret a script as a result of another bash shell interpreting:

    bash-script some args
    

    That bash will have passed _=/path/to/bash-scrip in the environment given to bash-script, and that's what the initial value of the $_ bash variable will be in the bash shell that interprets that script.

    $ env -i _=whatever bash -c 'echo "$_"'
    whatever
    
  • Now, if the invoking application doesn't pass a _ environment variable, the invoked bash shell will initialise $_ to the argv[0] it receives itself which could be bash, or /path/to/bash or /path/to/some-script or anything else (in the example above, that would be /bin/bash if the she-bang of the script was #! /bin/bash or /path/to/bash-script depending on the system).

    So that text is misleading as it describes the behaviour of the caller which bash has no control over. The application that invoked bash may very well not set $_ at all (in practice, only some shells and a few rare interactive applications do, execlp() doesn't for instance), or it could use it for something completely different (for instance ksh93 sets it to *pid*/path/to/command).

    $ env bash -c 'echo "$_"'
    /usr/bin/env   (env did not set it to /bin/bash, so the value we
                   get is the one passed to env by my interactive shell)
    $ ksh93 -c 'bash -c "echo \$_"'
    *20042*/bin/bash
    

2. Subsequently

The Subsequently is not very clear either. In practice, that's as soon as bash interprets a simple command in the current shell environment.

  • In the case of an interactive shell, that will be on the first simple command interpreted from /etc/bash.bashrc for instance.

    For instance, at the prompt of an interactive shell:

     $ echo "$_"
     ]      (the last arg of the last command from my ~/.bashrc)
     $ f() { echo test; }
     $ echo "$_"
     ]      (the command-line before had no simple command, so we get
             the last argument of that previous echo commandline)
     $ (: test)
     $ echo "$_"
     ]      (simple command, but in a sub-shell environment)
     $ : test
     $ echo "$_"
     test
    
  • For a non-interactive shell, it would be the first command in $BASH_ENV or of the code fed to that shell if $BASH_ENV is not set.

3. When Bash executes a command

The third point is something different and is hinted in the discussion above.

bash, like a few other shells will pass a _ environment variable to commands it executes that contains the path that bash used as the first argument to the execve() system calls.

$ env | grep '^_'
_=/usr/bin/env

4. When checking mail

The fourth point is described in more details in the description of the MAILPATH variable:

'MAILPATH'

A colon-separated list of filenames which the shell periodically checks for new mail.

Each list entry can specify the message that is printed when new mail arrives in the mail file by separating the filename from the message with a '?'. When used in the text of the message, '$_' expands to the name of the current mail file.

Example:

$ MAILCHECK=1 MAILPATH='/tmp/a?New mail in <$_>' bash
bash$ echo test >> /tmp/a
New mail in </tmp/a>

Try this simple example:

echo "$_"
echo "test"
echo "$_"

Run it by giving the absolute path (/home/$USERNAME/test.sh); output:

/home/$USERNAME/test.sh
test
test

First $_ contains the path you used to invoke the script and the second one contains the first argument to the middle echo. For the third item in the list, if you start a new terminal and type echo $_ it will show the variable containing the PATH which is usually used to lookup and invoke commands (in my Ubuntu machine) in a normal shell or absolute path to your .bashrs file in a login shell.

For the item four from bash man page:

When used in the text of the message, $_ expands to the name of the current mailfile.


For case 2, an example is worth a thousand words:

mkdir my_long_dir
cd $_

Does what you'd guess / hope for. Gives me a simple placeholder when I'm on the command line to save me having to repeate myself.

Tags:

Bash