In Bash, _
is a special parameter which is set to the value of the last argument every time a command is parsed. It also has the special property of not being exportable, which is enforced every time a command is executed (see bind_lastarg
in the Bash source code).
When you start Bash using bash --norc
, you get to the prompt without having executed any command; so _
, if it was present in the environment, has not been overwritten. When you start Bash from Bash, the parent Bash sets _
to the command being run in the child’s environment before starting it; like any other variable present in the environment at startup, _
therefore ends up being an exported variable, and since no command has been run, that variable hasn’t been “unexported”. This explains why your first export
contains it.
As soon as you run a command (your first export
in this case), _
is overwritten and loses its exported flag. This explains why your second export
doesn’t show it.
(Internally, _
is a variable like any other; so it can be set read-only or marked as an integer, with amusing results.)