web-dev-qa-db-ja.com

サブシェルに入っているかどうかをどのように検出できますか?

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!

私がサブシェルに入っているかどうかを検出する良い方法は何ですか?

28
jesse_b

Bashでは、$BASHPID$$

$ ( 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'。単純に( … ) Shellがフォークを最適化するため、機能しないように見えることがあります。追加の何もしないコマンドを試す( : ; sh -c 'echo $PPID'; : )サブシェルが複雑すぎて最適化できないと思わせるため。このアプローチでは、クレジットは スタックオーバーフローのJohn1024 になります。

44
derobert

BASH_SUBSHELL

BASH_SUBSHELL
シェルが各サブシェルまたはサブシェル環境内で1ずつ増加した場合
その環境で実行を開始します。初期値は0です。

$ echo $BASH_SUBSHELL
0
$ (echo $BASH_SUBSHELL)
1
39
Freddy

[これはコメントであるべきでしたが、私のコメントはモデレーターによって削除される傾向があるため、削除しても参照として使用できる回答として残ります]

BASH_SUBSHELLは、someサブシェルで1に設定されるだけで、すべてのサブシェルでは設定されないため、完全に信頼できません。

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

パイプラインコマンドが実行されるサブプロセスがreally実際のサブシェルではないと主張する前に、このman bashスニペットを検討してください:

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

そして、実用的な意味合い-それは、スクリプトの断片がサブプロセスで実行されるかどうかであり、本質的なことであり、用語の混乱ではありません。

この 質問 の回答ですでに説明した唯一の解決策は、$BASHPID$$と等しいかどうか、または移植可能ではあるが効率がはるかに低いかどうかを確認することです。

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