There are many ways of accidentally creating subshells, but a common one is piping to a loop:
n=0
printf "%s\n" {1..10} | while read i; do (( n+=i )); done
echo $n
# Bash specific: process substitution. Also try shopts like lastpipe.
n=0
while read i; do (( n+=i )); done < <(printf "%s\n" {1..10})
echo $n
In sh
, temporary files, FIFOs or file descriptors can be
used instead. When the output of the command can be stored to a variable
before entering the loop, here documents are a preferable
alternative.
n=0
SUMMANDS="1
2
3
4
5
6
7
8
9
10
"
while read i; do n=$(( n + i )); done <<SUMMANDS_HEREDOC_INPUT
$SUMMANDS
SUMMANDS_HEREDOC_INPUT
echo $n
With Bash 4.2+ you can also use shopt -s lastpipe
which
will change the pipe behaviour to be similar to Ksh and Zsh (see
Rationale below) as
long as job control is not active (e.g. inside a script):
#!/usr/bin/env bash
shopt -s lastpipe
n=0
printf "%s\n" {1..10} | while read i; do (( n+=i )); done
echo $n
Variables set in subshells are not available outside the subshell. This is a wide topic, and better described on the Wooledge Bash Wiki.
Here are some constructs that cause subshells (shellcheck may not
warn about all of them). In each case, you can replace
subshell1
by a command or function that sets a variable,
e.g. simply var=foo
, and the variable will appear to be
unset after the command is run. Similarly, you can replace
regular
with var=foo
, and it will be set
afterwards:
Pipelines:
subshell1 | subshell2 | subshell3 # Dash, Ash, Bash (default)
subshell1 | subshell2 | regular # Ksh, Zsh, Bash (with lastpipe=on and no job control)
Command substitution:
regular "$(subshell1)" "`subshell2`"
Process substitution:
regular <(subshell1) >(subshell2)
Some forms of grouping:
( subshell )
{ regular; }
Backgrounding:
subshell1 &
subshell2 &
Anything executed by external processes:
find . -exec subshell1 {} \;
find . -print0 | xargs -0 subshell2
sudo subshell3
su -c subshell4
This applies not only to setting variables, but also setting shell options and changing directories.
You can ignore this error if you don't care that the changes aren't reflected, because work on the value branches and shouldn't be recombined.
ShellCheck is a static analysis tool for shell scripts. This page is part of its documentation.