35

Could you explain the following sentences from Bash manual about $_, especially the parts in bold, maybe with some examples?

  1. At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list.

  2. Subsequently, expands to the last argument to the previous command, after expansion.

  3. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command.

  4. When checking mail, this parameter holds the name of the mail file.

jasonwryan
  • 73,126
Tim
  • 101,790
  • 4
    -1 question unclear & not thoroughly researched. What have you tried? Where did your expectations misalign with the text? Do you not honestly understand the word 'subsequently'? If a new user were to ask this same question I would have the same questions of them (despite my similar innate curiosity as to exactly how things work). – Jeff Schaller May 02 '16 at 02:41

3 Answers3

38

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>
  • Also this old chestnut: mkdir somedirectory && cd $_ puts you right in the directory you just created. – Raydot Oct 23 '20 at 20:52
11

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.

Vombat
  • 12,884
  • Thanks. (1) can you explain the third case with examples? (2) When I open a new gnome terminal tab, the output of echo $_ is EDITOR, and why is it? Which case does it follow? (3) Can you given an example of how to check mails, and of using $_ in this case? – Tim May 02 '16 at 00:00
  • Open your .bashrc and comment out every line there. Keep only a single line containing a command like echo "test" or similar. Save and close. Then run bash and immediately echo $_. This last echo should print test in the output. – Vombat May 02 '16 at 06:50
  • For the mail part I have not used Unix mail and don't have a working mail installed on my system now so cannot give you example. But should be obvious if you follow the description of MAILPATH in http://linuxcommand.org/lc3_man_pages/bash1.html – Vombat May 02 '16 at 07:18
  • 1
    FYI, the canonical, up-to-date version of the bash manual is: https://www.gnu.org/software/bash/manual/bashref.html . It's version 4.3 while the linuxcommand page was 4.1. – Jeff Schaller May 02 '16 at 09:47
9

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 repeat myself.


¹ at least here for a such a simple directory name and assuming $CDPATH is empty or unset and $IFS has not been modified from its default value. In the general case, you'd need something like mkdir -p -- $'-less tamed directory-/../foo/ *** Ah! *** /\n\n' && CDPATH= cd -P -- "$_", though that's orthogonal to this discussion.

dsz
  • 259