When is `_` an environment variable of a bash shell?

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 explore | 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.


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.


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.