1

I'm reading the book "Linux Command Line and Shell Scripting Bible" 3rd edition. On page 279, here I quote:

"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. Because of that, any variables you create in the script aren't available to the subshell command.

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

The last sentence confuses me. I tested with a simple script which exports some variables; the variables would not last after script exists however the script is invoked, from ./ path, or put the script in /usr/bin and run it without a path. It seems to me that there is no difference how it invoked regarding to subshell.

What did I miss?

1
  • From your quote, I'd say it would be the other way around. Current (unexported) variables would not be present to commands run with ./ but would be from /usr/bin.
    – Halfgaar
    Sep 18, 2015 at 19:48

1 Answer 1

3

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

You must log in to answer this question.

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