Comment savoir combien de sous-coquilles je suis?


40

Parfois, je fais des choses comme démarrer un sous-shell avec vim :sh. Comment puis-je savoir si je suis dans un sous-shell où exitje vais simplement me renvoyer d'un niveau, par opposition à être dans le shell le plus à l'extérieur où exitje vais me déconnecter ou fermer ma session.

Existe-t-il une sorte de totem d'Inception que je puisse faire tourner ou quelque chose pour savoir combien de niveaux je suis?



1
Salut! Un moyen rapide de voir si vous êtes dans une sous-coque ou non est de le faire echo $0. Si c'est le shell toplevel, il commencera probablement par un tiret. (Ceci est vrai au moins pour bash, et le tiret signifie qu'il s'agit d'un shell dit de connexion.)
jpaugh

Réponses:


40

Vous pouvez utiliser la commande pstree(fournie par défaut avec Ubuntu). Voici un exemple - actuellement, je n'ai qu'une seule fenêtre de terminal ouverte sur WSL:

User@Wsl:~$ pstree
init─┬─init───bash───pstree
     └─{init}

User@Wsl:~$ bash
User@Wsl:~$ sh
$ bash
User@Wsl:~$ pstree
init─┬─init───bash───bash───sh───bash───pstree
     └─{init}

Dans un environnement Linux / Ubuntu réel, l'arborescence des processus sera plus compliquée. Nous pouvons filtrer l’arbre par l’option -squi montrera les parents d’un processus sélectionné. Donc, notre commande pourrait être pstree -s $$, où $$est une variable d’environnement contenant le PID actuel:

User@Ubuntu:~$ pstree -s $$
systemd──lightdm──lightdm──upstart──gnome-terminal-──bash──pstree

User@Ubuntu:~$ bash
User@Ubuntu:~$ sh
$ bash
User@Ubuntu:~$ pstree -s $$
systemd──lightdm──lightdm──upstart──gnome-terminal-──bash──bash──sh──bash──pstree

Les références:


Ajouter un indicateur à l'invite du shell: Sur la base de l' idée de @ waltinator , afin d'avoir un compteur devant l'invite pour plusieurs obus différents lorsque le niveau est supérieur à un, j'ai ajouté les lignes affichées en dessous de la démo, au bas des fichiers d’ exécution commandes ( ~/.*rc) pertinents .

J'ai effectué des tests sur WSL, Ubuntu 16.04, Ubuntu 18.04 (serveur / ordinateur de bureau), Ubuntu 19.04, dans les sessions gnome-terminal, tty et ssh. Voici comment cela fonctionne:

entrez la description de l'image ici

La limitation est que: le compteur ne fonctionne que pour 13-14 niveaux de profondeur, en fonction du système d'exploitation. Je n'ai pas l'intention d'enquêter sur les raisons :)

  • bash> .bashrc:

    DEPTH=$(($(pstree -s $$ | sed -r 's/-+/\n/g' | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>') - 1))
    if (( DEPTH > 1 )); then PS1=$DEPTH:$PS1; fi
  • cshet tcsh> .cshrc:

    @ DEPTH = `pstree -s $$ | sed -r 's/-+/\n/g' | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>'` - 0
    if ( $DEPTH > 1 ) then; set prompt="$DEPTH":"$prompt"; endif
  • zsh> .zshrc:

    DEPTH=$(($(pstree -s $$ | sed -r 's/-+/\n/g' | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>') - 1))
    if (( DEPTH > 1 )); then PROMPT=$DEPTH:$PROMPT; fi
  • ksh> .kshrc:

    DEPTH=$(($(pstree -s $$ | sed -r 's/\-+/\n/g' | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>') - 0))
    if (( DEPTH > 1 )); then PS1="$DEPTH":"$PS1"'$ '; fi
  • shc'est en fait dashsur Ubuntu - ici les choses sont un peu compliquées et câblées (lisez les références ci-dessous pour plus d'informations):

    1. Editez le ~/.profilefichier et ajoutez la ligne suivante en bas:

      ENV=$HOME/.shrc; export ENV
    2. Créez le fichier ~/.shrcavec le contenu suivant, note kshlit également le $ENV:

      #!/bin/dash
      DEPTH=$(pstree -s $$ | sed -r 's/-+/\n/g' | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>')
      if [ "$0" != 'ksh' ]; then DEPTH=$((DEPTH - 1)); fi
      if [ "$DEPTH" -gt 1 ]; then export PS1='$DEPTH:\$ '; fi

Les références:


Créez une commande qui affichera la profondeur: Une autre option consiste à créer une commande shell qui affichera la profondeur. Pour cela, créez le fichier exécutable (il devrait donc être accessible à l’ensemble du système):/usr/local/bin/depth

sudo touch /usr/local/bin/depth
sudo chmod +x /usr/local/bin/depth

Editez le fichier avec votre éditeur favori et ajoutez les lignes suivantes comme contenu:

#!/bin/bash

SHELLS='(bash|zsh|sh|dash|ksh|csh|tcsh)'
DEPTH=$(pstree -s $$ | sed -r 's/-+/\n/g' | grep -Ec "\<$SHELLS\>")

if [[ $@ =~ -v ]]
then
        pstree -s $$ | sed -r 's/-+/\n/g' | grep -E "\<$SHELLS\>" | cat -n
fi

echo "DEPTH: $DEPTH"

[[ $DEPTH -gt 1 ]] && exit 0 || exit 1

Le script ci-dessus a deux options -vou --verbosequi produira une liste des shells impliqués. Et l’autre option permettant de vérifier si la profondeur est supérieure à 1 et sur cette base sera retourné exit 0ou exit 1, vous pouvez donc l’utiliser de cette manière depth && exit. Voici quelques exemples d'utilisation:

User@Ubuntu:~$ depth          # we are at the 1st level - bash
DEPTH: 1
User@Ubuntu:~$ sh           
$ csh                         # we are at the 2nd level - dash
Ubuntu:~% depth               # we are at the 3rd level - csh
DEPTH: 3
Ubuntu:~% ksh
$ depth -v                    # we are at the 4th level - ksh
     1  bash
     2  sh
     3  csh
     4  ksh
DEPTH: 4
$ depth && exit               # exit to the 3rd level - csh
DEPTH: 4
Ubuntu:~% depth && exit       # exit to the 2nd level - dash
DEPTH: 3
exit
$ depth && exit               # exit to the 1st level - bash
DEPTH: 2
User@Ubuntu:~$ depth && exit  # stay at the 1st level - bash
DEPTH: 1
User@Ubuntu:~$ depth && exit  # stay at the 1st level - bash
DEPTH: 1

Comparaison par les autres solutions: j'ai passé un peu plus de temps à découvrir certaines faiblesses des approches présentées ici. J'ai pu imaginer les deux cas suivants (les majuscules sont nécessaires pour une meilleure mise en évidence de la syntaxe):

  • Quand suou sudo -isont impliqués:

    User@Ubuntu:~$ ps | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh|su|sudo)\>'
    1
    User@Ubuntu:~$ echo $SHLVL
    1
    User@Ubuntu:~$ depth
    DEPTH: 1
    
    User@Ubuntu:~$ su spas
    Password:
    
    Spas@Ubuntu:~$ ps | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh|su|sudo)\>'
    1
    Spas@Ubuntu:~$ echo $SHLVL
    2
    Spas@Ubuntu:~$ depth
    DEPTH: 2
    
    Spas@Ubuntu:~$ sudo -i
    [sudo] password for spas:
    
    Root@Ubuntu:~# ps | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh|su|sudo)\>'
    3
    Root@Ubuntu:~# echo $SHLVL
    1
    Root@Ubuntu:~# depth
    DEPTH: 3
  • Lorsqu'un processus en arrière-plan est lancé:

    User@Ubuntu:~$ bash
    User@Ubuntu:~$ ps | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>'
    2
    User@Ubuntu:~$ echo $SHLVL
    2
    User@Ubuntu:~$ depth
    DEPTH: 2
    
    User@Ubuntu:~$ while true; do sleep 10; done &
    [1] 10886
    User@Ubuntu:~$ ps | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>'
    3
    User@Ubuntu:~$ echo $SHLVL
    2
    User@Ubuntu:~$ depth
    DEPTH: 2
    
    # Note: $SHLVL is not supported only by sh/dash.  
    #       It works with all other tested shells: bash, zsh, csh, tcsh, ksh
    
    User@Ubuntu:~$ sh
    $ ps | grep -Ec '\<(bash|zsh|sh|dash|ksh|csh|tcsh)\>'
    4
    $ echo $SHLVL
    2
    $ depth
    DEPTH: 3

Maintenant , je suis perplexe quant à la sortie , je suis sur mon système: systemd───xfce4-terminal───bash───pstree. Pourquoi est-ce ainsi?
val dit Réintégrer Monica le

1
@val: systemd est le processus init, le parent de tous les autres processus. Vous utilisez apparemment xfce4-terminal, ce qui a lancé un bashshell, dans lequel vous avez couru pstree, qui a rapporté lui-même et ses parents. Si vous parlez du manque d'étapes entre systemd et xfce4-terminal, il se peut que tout ce qui a été lancé que xfce4-terminal soit mort ou désavoué, auquel cas il serait hérité par init.
Nick Matteo

Une raison pour ne pas lire SHLVL? La portabilité entre les processus et les systèmes, je suppose, mais pstree peut ne pas être installé.
D. Ben Knoble

Bonjour, @ D.BenKnoble, comme indiqué dans la réponse de @ egmont , $SHLVLn'est pas pris en charge par certains shells. Plus spécifique, selon l’environnement de la démo ci-dessus, il n’est pas supporté uniquement par sh( dash) - et ce shell n’est pas du tout compté par cette variable. D'autre part pstreefait partie du paquet psmisc qui fournit également fuser, killallet quelques autres - c'est le composant principal d'Ubuntu - je ne l'ai pas installé sur les systèmes mentionnés dans cette réponse.
Pa4080

30

Vérifiez la valeur de la SHLVLvariable shell:

echo $SHLVL

Citant de bashla page de manuel de:

SHLVL  Incremented by one each time an instance of bash is started.

Il est également soutenu par zsh.


4
Mais sh n'est pas compté, donc l'exemple donné, avec sh, n'aurait pas augmenté SHLVL. C'est quand même quelque chose qui pourrait être utile pour ceux qui ne changent pas trop d'obus
ubfan1

3
@ ubfan1 sauf s'il existe une définition vimrc prioritaire, par :shdéfaut le shell de connexion de l'utilisateur, je pense (c'est vraiment une forme abrégée de :shellplutôt que le nom d'un fichier binaire de shell spécifique)
steeldriver

3
Je ne suis pas familier avec les détails de vim, mais je l' ai essayé :shde vimavant de poster cette réponse, et il a fait évoluer le niveau de shell pour moi. Mon shell de connexion est bash.
egmont le

9

Dans ma .bashrc, j’utilisais $SHLVLpour ajuster $PS1en ajoutant des +signes " " à ma $SUBSHELLvariable:

...
# set a variable to reflect SHLVL > 1 (Ubuntu 12.04)
if [[ $SHLVL -gt 1 ]] ; then
    export SUBSHELL="${SUBSHELL:+$SUBSHELL}+"
else
    export SUBSHELL=""
fi
...

if [[ "$color_prompt" = yes ]]; then
#             chroot?                       Depth      green       user@host nocolor  :   green      $PWD  red      (status) off   $ or # space             
    PS1='${debian_chroot:+($debian_chroot)}${SUBSHELL}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[1;31m\]($?)\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}${SUBSHELL}\u@\h:\w\$ '
fi
...

Ensuite, je peux voir à quelle profondeur je suis:

walt@bat:~(1)$ ed foo
263
!bash
+walt@bat:~(0)$ bash
++walt@bat:~(0)$ bash
+++walt@bat:~(0)$ exit
exit
++walt@bat:~(0)$ exit
exit
+walt@bat:~(0)$ exit
exit
!
q
walt@bat:~(0)$ 

4

awk:

# Count the occurrence of (sh)ells.
DEPTH_REGEX='^(ash|bash|busybox|csh|dash|fish|mksh|sh|tcsh|zsh)$'

DEPTH=$(/bin/ps -s $(/bin/ps -p $$ -osid --no-headers) -ocomm --no-headers | \
awk -v R=$DEPTH_REGEX '{for (A=1; A<=(NR-2); A++) {if ($A ~ R) {B++}}} END {print B}')

pgrep:

DEPTH=$(/usr/bin/pgrep -c -s $(/bin/ps -p $$ -osid --no-headers) '^(ash|bash|busybox|csh|dash|fish|mksh|sh|tcsh|zsh)$')

Vous pouvez placer l'une des deux versions dans un fichier et utiliser source pour rendre $ DEPTH disponible.

# Set 256 colors in terminal.
if [ -x /usr/bin/tput ] && [ "$(SHELL=/bin/sh tput colors)" -ge 8 ]; then
    export TERM="xterm-256color"
fi

# change these if you don't dig my colors!

NM="\[\033[0;1;37m\]"   #means no background and white lines
HI="\[\033[0;37m\]"     #change this for letter colors
SI="\[\033[38;5;202m\]" #this is for the current directory
NI="\[\033[0;1;30m\]"   #for @ symbol
IN="\[\033[0m\]"

# Count the occurrence of (sh)ells.
source /usr/share/shell-depth/depth

PS1="${NM}[${HI}\u${NI}@${HI}\h ${SI}\w${NM} \A](${HI}${DEPTH}${NM}): ${IN}"

2

Vous pouvez simplement utiliser, pssans aucun argument supplémentaire, l'ensemble de la pile de shell (y compris la pile actuelle). Il montrera également tous les jobs d'arrière-plan que vous avez commencés ainsi que pslui-même, mais il peut vous donner une estimation approximative de votre profondeur.


Cela fonctionne { echo hello world; ps; } &pour prouver la psréponse ci-dessus.
WinEunuuchs2Unix le

@ WinEunuuchs2Unix, je veux dire quelque chose comme ceci: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080

Existe-t-il un moyen d'imiter pstree -s $$ avec ps?
bac0n
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.