SC2031

koalaman edited this page Jan 20, 2018 · 5 revisions

ShellCheck

Clone this wiki locally

var was modified in a subshell. That change might be lost.

Problematic code:

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

Correct code:

# 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, a temp file (better if fifo or fd) can be used instead of process substitution. And if it's acceptable to do it with waiting, try Here Documents.

Rationale:

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    # Bash, Dash, Ash
subshell1 | subshell2 | regular      # Ksh, Zsh

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.

Exceptions

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.

Related resources: