Chapter 20. Subshells

Running a shell script launches another instance of the command processor. Just as your commands are interpreted at the command line prompt, similarly does a script batch process a list of commands in a file. Each shell script running is, in effect, a subprocess of the parent shell, the one that gives you the prompt at the console or in an xterm window.

A shell script can also launch subprocesses. These subshells let the script do parallel processing, in effect executing multiple subtasks simultaneously.

Command List in Parentheses

( command1; command2; command3; ... )

A command list embedded between parentheses runs as a subshell.

Note

Variables in a subshell are not visible outside the block of code in the subshell. They are not accessible to the parent process, to the shell that launched the subshell. These are, in effect, local variables.


Example 20-1. Variable scope in a subshell

   1 #!/bin/bash
   2 # subshell.sh
   3 
   4 echo
   5 
   6 outer_variable=Outer
   7 
   8 (
   9 inner_variable=Inner
  10 echo "From subshell, \"inner_variable\" = $inner_variable"
  11 echo "From subshell, \"outer\" = $outer_variable"
  12 )
  13 
  14 echo
  15 
  16 if [ -z "$inner_variable" ]
  17 then
  18   echo "inner_variable undefined in main body of shell"
  19 else
  20   echo "inner_variable defined in main body of shell"
  21 fi
  22 
  23 echo "From main body of shell, \"inner_variable\" = $inner_variable"
  24 # $inner_variable will show as uninitialized because
  25 # variables defined in a subshell are "local variables".
  26 
  27 echo
  28 
  29 exit 0

See also Example 32-1.

+

Directory changes made in a subshell do not carry over to the parent shell.


Example 20-2. List User Profiles

   1 #!/bin/bash
   2 # allprofs.sh: print all user profiles
   3 
   4 # This script written by Heiner Steven, and modified by the document author.
   5 
   6 FILE=.bashrc  #  File containing user profile,
   7               #+ was ".profile" in original script.
   8 
   9 for home in `awk -F: '{print $6}' /etc/passwd`
  10 do
  11   [ -d "$home" ] || continue    # If no home directory, go to next.
  12   [ -r "$home" ] || continue    # If not readable, go to next.
  13   (cd $home; [ -e $FILE ] && less $FILE)
  14 done
  15 
  16 #  When script terminates, there is no need to 'cd' back to original directory,
  17 #+ because 'cd $home' takes place in a subshell.
  18 
  19 exit 0

A subshell may be used to set up a "dedicated environment" for a command group.
   1 COMMAND1
   2 COMMAND2
   3 COMMAND3
   4 (
   5   IFS=:
   6   PATH=/bin
   7   unset TERMINFO
   8   set -C
   9   shift 5
  10   COMMAND4
  11   COMMAND5
  12   exit 3 # Only exits the subshell.
  13 )
  14 # The parent shell has not been affected, and the environment is preserved.
  15 COMMAND6
  16 COMMAND7
One application of this is testing whether a variable is defined.
   1 if (set -u; : $variable) 2> /dev/null
   2 then
   3   echo "Variable is set."
   4 fi     #  Variable has been set in current script,
   5        #+ or is an an internal Bash variable,
   6        #+ or is present in environment (has been exported).
   7 
   8 # Could also be written [[ ${variable-x} != x || ${variable-y} != y ]]
   9 # or                    [[ ${variable-x} != x$variable ]]
  10 # or                    [[ ${variable+x} = x ]])
Another application is checking for a lock file:
   1 if (set -C; : > lock_file) 2> /dev/null
   2 then
   3   echo "Another user is already running that script."
   4   exit 65
   5 fi   
   6 
   7 # Thanks, S.C.

Processes may execute in parallel within different subshells. This permits breaking a complex task into subcomponents processed concurrently.


Example 20-3. Running parallel processes in subshells

   1 	(cat list1 list2 list3 | sort | uniq > list123) &
   2 	(cat list4 list5 list6 | sort | uniq > list456) &
   3 	# Merges and sorts both sets of lists simultaneously.
   4 	# Running in background ensures parallel execution.
   5 	#
   6 	# Same effect as
   7 	#   cat list1 list2 list3 | sort | uniq > list123 &
   8 	#   cat list4 list5 list6 | sort | uniq > list456 &
   9 	
  10 	wait   # Don't execute the next command until subshells finish.
  11 	
  12 	diff list123 list456

Redirecting I/O to a subshell uses the "|" pipe operator, as in ls -al | (command).

Note

A command block between curly braces does not launch a subshell.

{ command1; command2; command3; ... }