こんにちは。
サブシェルによって実行される場合を少しまとめてみました:
- コマンドを括弧「()」で囲うとサブシェルによる実行となる1。
- パイプラインの前後はサブシェルによる実行となる。
サブシェルによる実行例
上記の 2 は勘違いしやすかったです。下記例を読み解くと、
command1; command2;{ command3; command4;} | { commmand5; command6;}; command7; command8
加えてコマンドグループにすると(すなわち brace「{}」で囲うと)一つのシェルで実行されるので、結局のところ下記のように実行されます:
- 元シェルで実行されるのは:
command1, command2
command7, command8
- サブシェルで実行されるのは:
command3, command4
command5, command6
サブシェル内の変数更新
次にサブシェル内の変数更新を考えると、これは元シェルへは伝播しません。下記例で末尾の echo $n
の出力は空となります。
$unset n;{n=0;printf'a\nb\n';} | cat;echo$na
b
$unset n;printf'a\nb\n' | (n=0;while read-r line;do n=$((n+1));done;echo$n);echo$n2
$unset n;printf'a\nb\n' | {n=0;while read-r line;do n=$((n+1));done;echo$n;};echo$n2
$
「サブシェル内の変数更新」を回避
元シェルでの変数更新であれば、「サブシェル内の変数更新」を回避できます。下記例で末尾の echo $n
の出力は 0
となります。
$unset n;{n=0;printf'a\nb\n';};echo$na
b
0
$unset n;{n=0;printf'a\nb\n' | cat;};echo$na
b
0
$
「ヒア・ドキュメント」の利用
同様に元シェルでの変数更新とするために、「ヒア・ドキュメント」を利用する方法もあります。下記例で末尾の echo $n
の出力は 2
となります。
$unset n;{n=0;while read-r line;do n=$((n+1));done<<EOT>$(printf'a\nb\n')>EOT
>echo$n;};echo$n2
2
$
もしくは、POSIX shell ではなく、Bash などでは Process Substitution を利用する方法もあります:
$unset n;{n=0;while read-r line;do n=$((n+1));done< <(printf'a\nb\n');echo$n;};echo$n2
2
$
こちらは勘違い(=問題化)しにくいと思います。 ↩