Subshells in Bash

Subshells are a fundamental concept in Bash scripting that can be both powerful and confusing, especially for beginners.
Subshells in Bash

On this page

Subshells are a fundamental concept in Bash scripting that can be both powerful and confusing, especially for beginners. They allow you to create isolated execution environments within your scripts, providing a way to effectively manage processes and execute commands without affecting the parent shell's environment. This article aims to provide a comprehensive understanding of subshells in Bash, including their creation, behavior, and practical applications.

What Exactly is a Subshell?

A subshell, also known as a child shell, is a separate instance of the shell that is spawned from the current shell process. It inherits the environment and variables from its parent shell but operates independently, allowing for isolated execution of commands and scripts. When a subshell is created, it runs in a separate process, distinct from the parent shell. This means that any changes made to the environment within the subshell, such as modifying variables or defining functions, are isolated and do not persist in the parent shell after the subshell terminates.

Creating a Subshell

There are several ways to create a subshell in Bash, each with its own nuances and use cases:

Parentheses or Curly Braces

The commands enclosed within parentheses are executed in a subshell. This is one of the most common and straightforward ways to create a subshell in Bash.

# Create a subshell
$ (pwd; ls; whoami)

Using curly braces {...} around a set of commands can also create a subshell:

$ { sleep 3; echo "Hello from subshell"; } 
# Subshell created

$ echo "Back in parent shell"

Command Substitution

# Assign the output of a subshell to a variable
$output=$(pwd; ls; whoami)

Command substitution creates a subshell and captures its output, which can be assigned to a variable or used in another command.

Explicit Subshell Invocation

The bash built-in command can be used to start a subshell and execute commands within it explicitly. The -c option allows you to specify the commands to be executed.

# Execute a subshell
$ bash -c "ls; whoami"

Relationship Between Parent Shell and Subshell

The parent shell and its subshells have a hierarchical relationship. As I have mentioned, the subshell inherits the environment variables, functions, and other settings from the parent shell, but any modifications made to the environment within the subshell are isolated and do not affect the parent shell.

To demonstrate this isolation, consider the following example:

# Parent shell
$ echo "Parent Shell: Value of  is $pshell"
  

# Create a subshell and modify pshell
$ (pshell=10; echo "Subshell: Value of pshell is $pshell")  

# Check the value of pshell in the parent shell
$ echo "Parent Shell: Value of pshell is $pshell"  

In this example, the variable pshell is first unset in the parent shell, meaning it has no value. Within the subshell created by the parentheses(), we assigned the value 10 to the variable pshell , and print its value. However, after the subshell terminates, the parent shell's value ofpshell remains unset, demonstrating the isolation of the subshell's environment.

It's important to note that subshells isolate the environment and affect the scope of variables and functions defined within them. Variables and functions defined in a subshell are not accessible outside of that subshell, even if the subshell is part of a larger script or function.

single subshell

How to Check if a Subshell Has Been Spawned

You can check if the current shell is a subshell by inspecting the value of the $BASH_SUBSHELL environment variable. This variable is set to a non-zero value in subshells, indicating the nesting level.

$ echo $BASH_SUBSHELL  
# Prints 0 in the parent shell, non-zero in subshells

Another great way to check if you have spawned a subshell using the ps --forest command. Now consider the following example:

$ bash
$ bash
$ ps --forest

Executing the ps --forest command shows the nesting of the subshell. We will talk about nested subshells in the following section.

Nested Subshells

Subshells can be nested, meaning that a subshell can spawn another subshell within itself. Each nested subshell inherits the environment from its parent subshell and the $BASH_SUBSHELL variable increments accordingly.

$ echo "Parent shell: $BASH_SUBSHELL" (
echo "Subshell 1: $BASH_SUBSHELL" (
echo "Subshell 2: $BASH_SUBSHELL" ))

Here is another example:

$ bash
$ bash
$ bash
$ ps --forest

Here we entered the bash command four times to create four subshells. You can exit out of each subshell shell by using the bash exit command.

Nested Subshells

Do Shell Scripts Run in Subshells?

The answer is yes! By default, shell scripts run in subshells. This means that any changes made to environment variables or other shell settings within the script are not propagated back to the parent shell. However, this behavior can be modified by using the source command or the dot (. ) builtin, which executes the script in the current shell context, allowing any modifications made to the environment to be reflected in the parent shell:

# Execute the script in the current shell context
$ source .bashrc

Alternatively, you can use the exec command within the script to replace the current shell process with the script process, preventing the script from running in a subshell.

$ exec bash
$ exec bash
$ exec bash
$ ps --forest

Making use of Subshells

Subshells can be utilized in various creative ways to achieve desired behaviors in Bash scripts and commands. Let’s look at a few ways we can make use of subshells

Putting Process Lists into the Background

By enclosing a list of commands in parentheses, you can create a subshell that runs the commands in the background, allowing you to continue working in the parent shell while the subshell processes run concurrently.

# Subshell running in the background

$ (sleep 10; echo "This ran in the background") &

Co-processing

Co-processing is similar to running a command in the background, the only difference is that they also create a subshell. Co-processes are useful for tasks that require parallel execution or communication between processes.

# Create a co-process that sleeps for 60 seconds
$ coproc sleep 60

Check out this article for an in-depth guide on co-processing.

Parallel Processing with Subshells

Subshells can also be used for parallel processing, allowing multiple tasks to run simultaneously. By enclosing each task in parentheses and separating them with the & operator, you can create subshells that run in parallel.

# Parallel processing example
$ (task1.sh) & (task2.sh) & (task3.sh) &

# Wait for all tasks to complete
$ wait

In the example above, task1.sh, task2.sh, and task3.sh are executed in separate subshells, running in parallel. The wait command ensures that the parent shell waits for all subshell processes to complete before continuing.

This technique can be especially useful for computationally intensive or time-consuming tasks, where parallelization can significantly improve overall execution time.

Why you should sometimes avoid using subshells in Bash

While subshells offer valuable features and functionality, there are situations where you may want to avoid creating unnecessary subshells. Creating a subshell involves spawning a new process, which can introduce some overhead, especially when done frequently. Additionally, changes made to environment variables or other shell settings within a subshell are not propagated back to the parent shell, which can sometimes lead to unexpected behavior or inconsistencies. Excessive use of subshells can also make scripts more complex and harder to read, especially for those unfamiliar with the concept. Furthermore, each subshell consumes additional system resources, such as memory and file descriptors, which can become problematic in resource-constrained environments or when dealing with large numbers of subshells.

Summing up

In this article, we've explored the concept of subshells in Bash, their creation methods, behavior, practical applications, and techniques for preventing scripts from running in subshells. Finally, we've also discussed situations where avoiding unnecessary subshells can be beneficial.

Subscribe to sysxplore newsletter and stay updated.

Don't miss anything. Get all the latest posts delivered straight to your inbox. It's free!
Great! Check your inbox and click the link to confirm your subscription.
Error! Please enter a valid email address!