10

Bash Manual says (manpage, my emphasis):

When Bash invokes an external command, the variable $_ is set to the full pathname of the command and passed to that command in its environment.

And (Special Parameters):

_

($_ , an underscore.) 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. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file.

  1. In a bash shell, I run:

    $ bash
    $ export | grep '_=' 
    

    According to the manual, _ should be an environment variable of the new bash shell. export is supposed to output all the environment variables of the new bash shell, but it doesn't output _. So I wonder whether _ is an environment variable of the new bash shell?

  2. Actually in any bash shell, the same thing happens

    $ export | grep '_='
    

    doesn't output anything. So I wonder if _ is ever an environment variable of a bash shell?

  3. For comparison:

    $ dash
    $ export  | grep '_='        
    export _='/bin/dash'
    

My post is inspired by Mike's comment and Stephane's reply.

3
  • 2
    It's a shell variable, and it's passed to the command's environment; it's not necessarily exported to the shell's environment. export is a builtin, but if you use printenv _, it will show you how it was invoked: /usr/bin/printenv on this system. Apr 10, 2018 at 10:50
  • Note that bash -c export | grep _= (from Bash), will show how the parent shell invoked the bash command, even though $_ is unset in the parent. Apr 10, 2018 at 10:52
  • See also unix.stackexchange.com/questions/293302 .
    – JdeBP
    Apr 16, 2018 at 4:23

3 Answers 3

14

Yes, _ is an environment variable of the new Bash shell; you can see that by running

tr '\0' '\n' < /proc/$$/environ | grep _=

inside the shell: that shows the contents of the shell’s initial environment. You won’t see it in the first shell because there wasn’t a previous shell to set it before it started.

Expanding $_ inside Bash refers to the _ special parameter, which expands to the last argument of the previous command. (Internally Bash handles this by using a _ shell variable, which is updated every time a command is parsed, but that’s really an implementation detail. It is “unexported” every time a command is parsed.) export doesn’t show _ because it isn’t a variable which is marked as exported; you can however see it in the output of set.

In the first example, the new Bash shell parses and executes the commands in its startup files, so when running export | grep '_=', _ has already been overwritten and marked as not exported.

In the dash example, it doesn't seem to execute any start-up file, so you’re seeing the variable as an environment variable that was set by Bash before running dash.

2
9

export without arguments lists all exported variables. _ is not a variable, but is listed as a special parameter.

Somewhat confusingly, _ would also be a valid name for a variable, unlike the names of the other special parameters. At least Bash 4.4 allows assignments to it, without complaints. It's just not useful because the special effect immediately overrides the value.

4
  • 2
    Have fun trying to use _ as a variable though ;-). It’s effectively write-only and the value is lost immediately. Apr 9, 2018 at 21:06
  • 1
    Also, internally Bash does treat _ as a variable, which is why it appears in the output of set. However it can’t be marked as exported as far as I can determine. Apr 9, 2018 at 21:07
  • 2
    @StephenKitt, but Bash 4.4 allows marking it read-only. Or an integer. With somewhat hilarious results.
    – ilkkachu
    Apr 9, 2018 at 21:10
  • 1
    Ha, nice find, that’s rather amusing! Apr 9, 2018 at 21:14
5

Not every shell variable es marked as exported as you can see in the output of declare -p.

It does not make any sense for bash to mark $_ as exported because it automatically adds this variable to the environment of child processes but with a different value than the one it has in the shell (at that moment).

Showing it as exported would just confuse the user about what is going to happen with the environment of external commands.

All the "runtime variables" BASH* are not exported.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .