"Command substitution creates what's called a subshell to run the enclosed command. A subshell is a separate child shell generated from the shell that's running the script.
This is talking about constructions like:
echo "$(date) `uname -r`"
In this example I am invoking two subshells using the two different supported notations for subshells. The first subshell runs the date
command and the second subshell runs the uname
command. The echo
command is executed by the original shell after both subshells have terminated.
Because of that, any variables you create in the script aren't available to the subshell command.
Here the author got it backwards. Variables created before invoking the subshell are available to the subshell. Variables created inside the subshell are only available to the subshell and will be gone once the subshell terminates.
Subshells are also created if you run a command from the command prompt using the ./ path, but they aren't created if you just run the command without a path."
It is very unclear what the author is trying to say here. If you invoke an external command the following will happen regardless of whether it is invoked with or without a path:
- The shell
fork
s to create a new process.
- The newly created child process performs any I/O redirection you may have specified.
- The child process execve the external command at which point the processes ceases to be a shell and becomes whatever the external program is. (Which of course could be a shell script).
It is a new process, so any variables set by the external command will be unavailable to the parent process just like they would if it had been a subshell.
The author might have been referring to one of the following three scenarios:
- The external command is an incorrectly formatted script, in which case the calling shell may be working around the incorrect formatting by making a guess about which shell to use to interpret the script. (This scenario is so unpredictable, that I strongly recommend against relying on it. The correct way to find the interpreter is by having a
#!
line at the start of the script.)
- You might be invoking an internal command such as
read
, which syntactically looks like an external command found by searching the PATH
. But even though it syntactically looks the same, it will behave differently. Since read
will set a variable intended to be used by following commands, read
itself has to happen in the current process without any fork
call. (Even internal commands that could safely be invoked as a subprocess are better done in the current process for performance reasons. Calling fork
is sort of expensive.)
- You might be sourcing shell commands using
. file
or source file
. In this case file
is not a script, but it is somewhat similar. All of the commands in file
will be invoked by the current shell without first calling fork
. (But of course the commands inside file
could trigger fork
calls). In this case any #!
line in file
would be ignored, and any variables set by file
would be available to your current shell.
./
but would be from/usr/bin
.