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


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?


4 Answers 4


The underscore is actually a special shell variable. What you are seeing here is the underscore (_) variable with a value of var=99. It is readonly, and maintained by the shell. It is:

  • Set at shell startup, containing the absolute file name of the shell or script being executed as passed in the argument list.
  • After that, it expands to the last argument to the previous command, after expansion.
  • When checking mail, this parameter holds the name of the mail file.
  • It is also set to the full pathname of each command executed and placed in the environment exported to that command.

Your example falls into the second category. You typed:

set -a var=99  

So the last argument there was var=99, and that was the value you were setting (you weren't setting var to 99). Hence, _ was set to that. And that is what is being reported:


It's a bit confusing, but the first = indicates the assignment to the variable _, and the second is part of the value.

It is also worth mentioning that the -a option to set will cause all subsequently defined variables to be exported.


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

$ declare -p test_var

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 "$_"

$ 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

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 "$_"

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

$ echo "$_"

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'

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

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

The right answer would depend on the shell you're using:

  • for bash, whatever @BobEager said applies
  • for zsh, it is only set to the last argument of the previous command, and to the full pathname of the command in the command's environment
  • some shells like dash only set this variable when running an interactive session

There may be other particularities in other shells as well. As such, $_ is not defined in POSIX, so one should be aware of potential portability issues when using it.

Side note: if your intention is to assign the value of 99 to var and make it available in the environment of subsequent subprocesses, the correct syntax to achieve this is:

export var=99
  • 2
    An also correct syntax is unset var;set -a; var=99. Works in all resonable shells (not csh et al).
    – user232326
    Jul 11, 2017 at 20:10
  • 1
    @Arrow Although according to the last line of Bob Eager's answer this would make all variables subsequently set/modified exported, which may not be what you want.
    – TripeHound
    Jul 12, 2017 at 12:40

And I note echo $var will give nothing.  …  What’s happen?

In the not-so-fine print of bash(1), we find

set [--abefhkmnptuvxBCEHPT] [-o option-name] [arg …]
set  [+abefhkmnptuvxBCEHPT]  [+o option-name] [arg …]

    When options are specified, they set or unset shell attributes.  Any arguments remaining after option processing are treated as values for the positional parameters and are assigned, in order, to $1, $2, … $n.

Which means, set -a var=99 doesn’t set environment variable var to 99; it sets $1 to var=99.  Try echo "$1" or echo "$*" and you’ll see.


You must log in to answer this question.

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