First, we think of the export (equivalent to declare-x) command:
$ export | grep ' Declare-x _= ' |
Not found, so the conclusion is _ is not an environment variable? Of course it's not that simple, otherwise this article should be over. And don't forget the env (or printenv) command:
$ env | grep ' _= ' _=/usr/bin/env |
What to do, _ is not the environment variable? Who said that right? However, there are more bizarre:
$ bash-c "Export | grep ' Declare-x _= ' " Declare-x _= "/bin/bash" $ bash-c ":; Export | grep ' Declare-x _= ' " |
When executed in a bash-c manner and export is the first command, _ is considered an environment variable, and once another command has been executed, _ becomes a non-environment variable.
In order to find the answer, I had to go through the Bash source, the following directly tell me from the source of the three-point conclusion:
1. When Bash starts,
When bash starts, if there is _ in the environment variable of the bash's parent process, bash must also inherit the environment variable, which is an OS-level implementation that bash cannot violate. If the parent process does not have an _ environment variable, Bash creates the environment variable itself and assigns its initial value to $ A, and the associated code is in the Initialize_shell_variables function in VARIABLES.C:
/* Set up initial value of $_ */
Temp_var = Set_if_not ("_", Dollar_vars[0]) # If translated with Bash code the C code is: [[Z-$_]] && export _=$0
2. After executing arbitrary commands
The value of the _ variable is the last parameter of the last command, so it is obvious that bash will have to reassign the variable after executing any command, but in addition to the assignment, bash does one thing by marking the _ variable as a non-environment variable. There is a bind_lastarg function in EXECUTE_CMD.C to do both things:
static void
Bind_lastarg (ARG)
Char *arg;
{
Shell_var *var;
if (arg = = 0)
arg = "";
var = bind_variable ("_", arg, 0); # This code is to set the value of _ to the last parameter of the last command
Vunsetattr (Var, att_exported); # This is to mark _ as a non-environment variable
}
3. Before executing external commands
Before executing the external command, Bash will specifically set the value of _ to the path of the external command, and mark the _ as an environment variable. The relevant code is in the Execute_disk_command function in EXECUTE_CMD.C:
Put_command_name_into_env (command);
The implementation of this put_command_name_into_env function is in VARIABLES.C:
void
Put_command_name_into_env (Command_name)
Char *command_name;
{
Update_export_env_inplace ("_=", 2, Command_name); # This code is translated into Bash code: Export _= $command, command is the path of the external command
}
Three points are done, and then look back at the seemingly bizarre code examples.
Why does the export not see _ and env can see?
Because we execute the test code normally under the interactive shell, the interactive shell executes the. BASHRC startup script, so when we execute the Export command, we must have executed another command, after executing any command, Bash will mark _ as a non-environment variable, Without seeing _ in export, we can open an interactive Shell that does not execute the RC script and then look at:
$ bash--NORC $ export | grep ' Declare-x _= ' # Bash is also an external command, so as stated in the 3rd conclusion above, now the shell's parent shell will mark _ as an environment variable, # at the same time set its value to bash's path, now this Shell inherits _ this environment variable, so here can see _ Declare-x _= "/bin/bash" $ export | grep ' Declare-x _= ' # When the previous command is executed, as stated in the 2nd conclusion above, the current Shell will mark _ as a non-environment variable, so here _ is not an environment variable. |
Env can always see that because Env is an external command, Bash always marks _ as an environment variable before executing it, and sets the value of _ to/usr/bin/env.
Why even export _; Export also cannot see _
Although the export _ will mark _ as an environment variable, but because export _ is also a command, according to the 2nd conclusion above, when the export _ execution ended, Bash again Mark _ as a non-environment variable.
and prove the 1th conclusion through the code demonstration.
To prove that bash inherits the environment variables of the parent process and that bash will establish the environment variable at startup when the parent process does not have an _ environment variable.
$ env bash-c ' echo $_ ' # env inherits the environment variable that the current shell has set up for it, and then passes it to its child process shell /usr/bin/env $ env _=1 bash-c ' echo $_ ' # env was re-assigned _ and then passed to bash 1 $ Python # Cannot delete _ variable in Bash, the env command can only be changed to a value and cannot be deleted, so here is a Python demo >>> Import OS >>> Os.environ.pop ("_") # Delete _ environment variable '/library/frameworks/python.framework/versions/2.7/bin/python ' >>> os.system (' Bash-c ' echo $_ "') # Bash found no _ variable at startup, so it was assigned a value of $ Sh |
What did you say in manual?
Take a look at manual. What information is omitted from the $_ paragraph:
At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the En vironment or argument list
What is said here is that if you do not inherit from the parent process to _ when the performance, if you inherit _, will not do this thing.
Subsequently, expands to the last argument to the previous command, after expansion.
It does not say that you will mark _ as a non-environment variable at the same time.
Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command.
Not clearly, the command here only refers to external commands.
Summarize
To summarize, the answer to this question is: _ When Bash is just started (before executing the first command) or the moment before executing an external command is an environment variable, and at other times it is a non-environment variable.
The _ in Bash is not an environment variable