POSIXシェルスクリプトパイプラインで実行された最後の関数が変数値を保持しないのはなぜですか?

POSIXシェルスクリプトパイプラインで実行された最後の関数が変数値を保持しないのはなぜですか?

次のスクリプトを想定します。

#!/bin/sh

func1() {
  eval $1'=$(cat)'
  eval echo "Value$2 inside function : \$$1"
}

func1 x 1 <<'HEREDOC'
Hello World
HEREDOC

echo "Value1 outside function: $x"

x=""

echo "Hello World" | func1 x 2

echo "Value2 outside function: $x"

Bash 4.3.43-4.fc25では、出力は次のようになります。

Value1 inside function : Hello World
Value1 outside function: Hello World
Value2 inside function : Hello World
Value2 outside function: 

bashismを使ってshopt -s lastpipe最後の行に「Hello World」も表示させるようにしましたが、直感的にはこれが自動的に発生しない理由を理解できません。これが期待されるか。

POSIX標準は実際にはこのトピックを議論していないようです。http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_02

ベストアンサー1

歴史的に、ボンシェルそしてコーエンシェル別の方法で行動してください。パイプの両側が異なるプロセスで並列に実行されるため、残りのスクリプト全体で両方の変数割り当てを維持することは不可能です。与えられた場合、unset a; a=b | a=c変数はa設定されていないか、等しいかb等しいことがcあります。bc

Bourneシェルでは、パイプ演算子の各側面がサブシェル(つまり別々のシェルプロセス)で実行されるため、変数の割り当ては維持されません。 Korn シェルでは、パイプの右側が元のシェルプロセスで実行されます。

POSIXは、既存の動作が他の多くの場合と同様に、両方の動作を許可します。このセクションの説明に従って「シェル実行環境」:

マルチコマンドパイプラインの各コマンドはサブシェル環境にありますが、拡張機能によってパイプラインの一部またはすべてのコマンドを現在の環境で実行できます。他のすべてのコマンドは、現在のシェル環境で実行されます。

一部のシステムでは、現在の環境でパイプラインの最終段階をすでに実装しており、次のコマンドを実行しています。

command | read foo

foo現在の環境で変数を設定します。この拡張は許可されますが、必須ではありません。したがって、シェルプログラマはパイプがサブシェル環境にありますが、それに依存してはいけないことを考慮する必要があります。

実際に生成されたプロセスの数を注意深く調べない限り、同じように機能するいくつかの最適化を除いて、最後のコマンド以外のコマンドは常にサブシェルで実行されます。つまり、シェルにはunset a; a=b | true; echo $a印刷がありませんb

ash、dash、bash、pdksh、mkshなどの他のほとんどのBourne様シェルは、Bourneシェルのように動作します。 ZshはKornシェルのように動作します。最新バージョンのbashでは、kshアクションに切り替えることができますshopt -s lastpipe

おすすめ記事