web-dev-qa-db-fra.com

Comment puis-je détecter si je suis dans un sous-shell?

J'essaie d'écrire une fonction pour remplacer la fonctionnalité du exit intégré pour m'empêcher de quitter le terminal.

J'ai essayé d'utiliser la variable d'environnement SHLVL mais elle ne semble pas changer dans les sous-coquilles:

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

Ma fonction est la suivante:

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

Cela ne me permettra pas d'utiliser exit dans les sous-coquilles:

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

Quelle est la bonne méthode pour détecter si je suis ou non dans un sous-shell?

28
jesse_b

En bash, vous pouvez comparer $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

Si vous n'êtes pas en bash, $$ devrait rester le même dans un sous-shell, vous auriez donc besoin d'une autre façon d'obtenir votre ID de processus réel.

Une façon d'obtenir votre pid réel est sh -c 'echo $PPID'. Si vous venez de mettre cela dans un simple ( … ) il peut sembler que cela ne fonctionne pas, car votre Shell a optimisé la fourche. Essayez des commandes supplémentaires sans opération ( : ; sh -c 'echo $PPID'; : ) pour faire croire que le sous-shell est trop compliqué pour être optimisé. Le crédit revient à John1024 on Stack Overflow pour cette approche.

44
derobert

Que diriez-vous BASH_SUBSHELL?

BASH_SUBSHELL
Incrémenté d'un dans chaque environnement de sous-shell ou sous-shell lorsque le shell
Commence à s'exécuter dans cet environnement. La valeur initiale est 0.

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

[cela aurait dû être un commentaire, mais mes commentaires ont tendance à être supprimés par les modérateurs, donc cela restera une réponse que je pourrais utiliser comme référence même s'il est supprimé]

En utilisant BASH_SUBSHELL n'est absolument pas fiable car il n'est défini que sur 1 dans certains sous-coquilles, pas dans toutes les sous-coquilles.

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

Avant de prétendre que le sous-processus dans lequel une commande de pipeline est exécutée n'est pas un véritable sous-shell réel, considérez ceci man bash extrait:

Chaque commande d'un pipeline est exécutée en tant que processus distinct (c'est-à-dire dans un sous-shell).

et les implications pratiques - c'est si un fragment de script est exécuté sous-processus ou non, ce qui est essentiel, pas une petite gêne terminologique.

La seule solution, comme déjà expliqué dans les réponses à cette question est de vérifier si $BASHPID équivaut à $$ ou, de manière portable mais beaucoup moins efficace:

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