Bashで私がサブシェルにあるかどうかをどのように検出しますか?

Bashで私がサブシェルにあるかどうかをどのように検出しますか?

exit端末のシャットダウンを防ぐために、組み込み関数の機能を置き換える関数を作成しようとしています。

環境変数を試してみましたSHLVLが、サブシェルでは変更されていないようです。

$ echo $SHLVL
1
$ ( echo $SHLVL )
1
$ bash -c 'echo $SHLVL'
2

私の機能は次のとおりです。

exit () {
    if [[ $SHLVL -eq 1 ]]; then
        printf '%s\n' "Nice try!" >&2
    else
        command exit
    fi
}

exitただし、この方法ではサブシェルでは使用できません。

$ exit
Nice try!
$ (exit)
Nice try!

私がサブシェルにいるかどうかを検出する良い方法はありますか?

答え1

$BASHPIDBashでは比較できます$$

$ ( if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi )
subshell
$   if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi
not subshell

Bashを使用しない場合は、$$サブシェルで変更されていないままにする必要があるため、実際のプロセスIDを取得するには別の方法が必要です。

実際のpidを取得する1つの方法はsh -c 'echo $PPID'。ただ普通の場所に置いておくと、( … )シェルはすでにフォークに最適化されており、機能しないように見えることがあります。追加のno-opコマンドを試して、( : ; sh -c 'echo $PPID'; : )サブシェルが最適化するには複雑すぎると思います。信用は行くスタックオーバーフローのJohn1024この方法の場合。

答え2

どうですかBASH_SUBSHELL

BASH_SUBSHELL その環境で
      シェルが実行を開始するときに、各サブシェルまたはサブシェル環境に 1 つを追加します。
初期値は0です。

$ echo $BASH_SUBSHELL
0
$ (echo $BASH_SUBSHELL)
1

答え3

[コメントでなければなりませんが、私が書いたコメントは、オペレータによって削除される傾向があり、答えとして残り、削除されても参照用に使用できます。]

1にのみ設定できるため、使用するのにBASH_SUBSHELL完全に信頼できません。一部サブシェルですが、すべてのサブシェルにあるわけではありません。

$ (echo $BASH_SUBSHELL)
1
$ echo $BASH_SUBSHELL | cat
0

パイプコマンドを実行する子プロセスがパイプラインではないことを宣言する場合本物真のサブシェルの場合は、次のman bashコードスニペットを検討してください。

パイプラインの各コマンドは、別々のプロセス(つまりサブシェルで)で実行されます。

そして実用的な意味 - 重要なのは、用語の議論ではなく、スクリプトの断片が子プロセスで実行されるかどうかです。

この質問に対する回答ですでに説明したように、唯一の解決策は質問$BASHPID移植性と同じです$$が、はるかに効率が低いことを確認することです。

if [ "$(exec sh -c 'echo "$PPID"')" != "$$" ]; then
    echo you\'re in a subshell
fi

関連情報