> ## Content Index
> Fetch the complete content index at: https://www.binarysludge.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# BASH Subshell Gotchas
- URL: https://www.binarysludge.com/2015/06/30/bash-subshell-gotchas/
- Published: 2015-06-30T06:11:42.000Z
- Updated: 2015-06-30T06:20:00.000Z
- Author: Andrew Martin
- Tags: bash, System Administration, #Import 2026-10-22 12:56

It's always helpful to remember that not all BASH operations are created equal. In dicing with configuring an environment I failed to recall the first rule of BASH scripting: Know Your Subshells!

In this exampe `n` is always 0\. This is because the increment operation is in a subshell, and variables do not propagate back to their parent shells.

```
n=0
cat file | while read line; do (( n++ )); done
echo $n

```

> Environment variables, as well as the current directory, is only inherited parent-to-child. Changes to a child’s environment are not reflect in the parent. Any time a shell forks, changes done in the forked process are confined to that process and its children.

From the [Advanced Bash-Scripting Guide on Subshells](http://www.tldp.org/LDP/abs/html/subshells.html?ref=binarysludge.com):

> In general, an external command in a script forks off a subprocess, whereas a Bash builtin does not. For this reason, builtins execute more quickly and use fewer system resources than their external command equivalents.

So, where does BASH create a subshell?

- executing a program or script
- for every invocation in a pipeline
- any time a new shell is started
- for background commands and coprocesses
- in-shell command expansion
- in-shell process substitution
- explicit subshells

So to prevent a subshell being forked, use [exec](http://www.tldp.org/LDP/abs/html/subshells.html?ref=binarysludge.com#FTN.AEN18102) or a builtin.

To wit:

```
# Executing other programs or scripts
./setmyfoo
foo=bar ./something

# Anywhere in a pipeline in Bash
true | foo=bar | true

# In any command that executes new shells
awk '{ system("foo=bar") }'h
find . -exec bash -c 'foo=bar' \;

# In backgrounded commands and coprocs:
foo=bar &
coproc foo=bar

# In command expansion
true "$(foo=bar)"

# In process substitution
true < <(foo=bar)

# In commands explicitly subshelled with ()
( foo=bar )

```

Source: [Vidar’s Blog » Why Bash is like that: Subshells](http://www.vidarholen.net/contents/blog/?p=178&ref=binarysludge.com) \- contains further examples