"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
forks 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.