Meaning of _= in Environment Variables – Explained

environment-variablesset

After I run the set -a var=99, I can find a sentence in the output of set:

...
TERM=xterm
UID=0
USER=root
VIRTUAL_ENV_DISABLE_PROMPT=1
_=var=99
colors=/etc/DIR_COLORS
...

Could anyone tell me what "_=" means?

I note that echo $var will give nothing. If I run the set -a, then the set won't include this variable again. What is happening?

Best Answer

The _= at the start of a line in the output of plain set means that there is a variable called _ (underscore) in bash. And its value is what follows the =. Like setting a variable named test_var to 12345 will yield (in bash):

$ test_var=12345

$ set | grep test_var
test_var=12345

$ declare -p test_var
test_var=12345

That special bash variable will be set to the last argument of the previous line:

$ echo "Testing some" "values" "in the shell" "(only bash)" "_=myvar=hello"
Testing some values in the shell (only bash) _=myvar=hello

$ echo "$_"
_=myvar=hello

$ echo "Testing some" "values" "in the shell" "(only bash)" "_=myvar=hello"
Testing some values in the shell (only bash) _=myvar=hello

$ declare -p _
declare -- _="_=myvar=hello"

Note the two = in that output.


Longer description

When you execute set -a var=99, three distinct things happen (related to your question):

  1. A set option (-a) (reverse by doing set +a) to export vars.
  2. Positional parameters are "set" to what follows the options ($1 set to var=99).
  3. The shell variable underscore $_ is set to the last parameter (expanded).

set -a

The execution of set -a marks all subsequent (new or changed) variables as exported (in all shells, except csh and some related shells).

$ set -a
$ myvariable=wer
$ env | grep myvariable
myvariable=wer

To recover from this setting, just change the - to a +:

$ set +a
$ unset myvariable              # to erase it from the environment if it
                                # was exported before the change of set +a
$ myvariable=456544654          # A new value of the variable.
$ env | grep "variable"         # No output means the var does not exist
                                # in the current environment

set var=99

Which should actually be set -- var=99 to avoid interpretation of an option (and set has a lot) with values that start with a dash (-).

Sets the list of arguments (the list of positional parameters) to that after the --. That is valid in all reasonable shells (not in csh et al). Positional arguments are printed with "$@" (or similar "$*", not equal).

$ set -- a=1 b=2 c=3
$ echo "$@"
a=1 b=2 c=3

#_=last_argument And, the value of the internal shell variable _ change to the last argument of the executed line. That is NOT true in almost all shells (jsh, ash, yash, dash, lksh, mksh, ksh93, attsh and of course csh and tcsh) except bash.

$ echo one two last argument
one two last argument

$ echo "$_"
argument

$ echo This is a new: last_argument
This is a new: last_argument

$ echo "$_"
last_argument

Please note that the value in $_ is the value after expansion:

$ a="A New_Argument"
$ echo Something else to test: "$a"
Something else to test: A New_Argument

$ echo "$_"
A New_Argument

That's why when you execute:

$ set -a myvar=99; set | grep 'myvar'
_=myvar=99

You get a description of the shell variable '$_'. This also works:

$  set -a myvar=99; declare -p _
declare -- _="myvar=99"
Related Question