Bourne to Bourne Again Shell Forward Compatibility

Pipeline Subprocesses

For the following examples, install AT&T and MirOS Korn Shell, Debian Almquist Shell and Bourne Again Shell:

apt install ksh mksh dash bash

Execute the following three examples in each shell and compare the output.

First Example

Try the following example in every shell variant:

s=0
seq 3 | while read i ; do
    s=$((s+i))
done
echo $s

The output will be:

  • ksh: 6
  • mksh: 0
  • dash: 0
  • bash: 0

Second Example

Try the following example in every shell variant:

s=0
while read i ; do
    s=$((s+i))
done << EOF
$(seq 3)
EOF
echo $s

The output will be:

  • ksh: 6
  • mksh: 6
  • dash: 6
  • bash: 6

The difference between the two examples is: If a pipeline has multiple components A, B, … of a pipeline A | B | ... all shells except AT&T Korn Shell run all components after A in sub-processes (so called „sub-shells“).

In the first example, run in AT&T Korn Shell, the pipe component while does not spawn a sub-process, and changes to s are visible to the main process.

The second example renders the same result in all shell variants, since the pipeline has only one component.

Third Example

Try the following example in every shell variant:

s=0 ; i=0
while [ "$i" -lt 3 ] ; do
    i=$((i+1)
    s=$((s+i))
done | cat
echo $s

The output will be:

  • ksh: 0
  • mksh: 0
  • dash: 0
  • bash: 0

In this example, summands and sum are calculated in the first of a two-component pipeline. The second component is a cat command, it is just there so the pipeline has more than one component. As soon as a pipeline has more than one component, the shell executes the first component in a sub-process, and the changes to s are not visible in the main program. The result of ksh is remarkable if compared to that of the first example, where the change to s was visible.

Advice

  1. Do not rely on pipeline components to be able to modify global variables. Communicate data from pipeline components to the main program without global variables.
  2. Do not rely on pipeline components not modifying global variables; do not name variables that should be local to the pipeline component same as existing global variables.