46 votes

Comment puis-je savoir à combien de sous-coquilles je me trouve ?

Parfois, je fais des choses telles que lancer un sous-shellShell à partir de vim avec :sh . Comment puis-je savoir si je me trouve dans un sous-Shell où exit me renverra simplement à un niveau supérieur, au lieu d'être dans le Shell le plus éloigné, où exit me déconnectera ou fermera ma session.

Existe-t-il une sorte de totem d'Inception que je peux faire tourner ou quelque chose du genre pour savoir à combien de niveaux je me trouve ?

45voto

pa4080 Points 27038

Vous pouvez utiliser la commande pstree (qui est fourni 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
initinitbashpstree
     {init}

User@Wsl:~$ bash
User@Wsl:~$ sh
$ bash
User@Wsl:~$ pstree
initinitbashbashshbashpstree
     {init}

Dans un environnement Linux/Ubuntu réel, l'arborescence des processus sera plus complexe. Nous pouvons filtrer l'arbre par l'option -s qui affichera les parents d'un processus sélectionné. Notre commande pourrait donc être pstree -s $$$$ est une variable d'environnement qui contient le PID actuel :

User@Ubuntu:~$ pstree -s $$
systemdlightdmlightdmupstartgnome-terminal-bashpstree

User@Ubuntu:~$ bash
User@Ubuntu:~$ sh
$ bash
User@Ubuntu:~$ pstree -s $$
systemdlightdmlightdmupstartgnome-terminal-bashbashshbashpstree

Références :


Ajouter un indicateur à l'invite du Shell : Sur la base de l'article de @waltinator idée afin d'avoir un compteur à l'avant de l'invite pour plusieurs coquilles différentes lorsque le niveau est plus profond qu'un, j'ai ajouté les lignes, montrées sous la démo, au bas de l'invite concernée exécuter des commandes ( ~/.*rc ).

J'ai fait des tests sur WSL, Ubuntu 16.04, Ubuntu 18.04 (serveur/bureau), Ubuntu 19.04, en session gnome-terminal, tty et ssh. Voici comment cela fonctionne :

enter image description here

La limitation est la suivante : le compteur ne fonctionne que pour 13-14 niveaux de profondeur, selon le système d'exploitation. Je n'ai pas l'intention d'en rechercher 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
  • csh y 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
  • sh qui est en fait dash sur Ubuntu - ici les choses sont un peu compliquées et câblées (lire les références ci-dessous pour plus d'informations) :

    1. Modifier le ~/.profile et ajoutez la ligne suivante à la fin du fichier :

      ENV=$HOME/.shrc; export ENV
    2. Créer le fichier ~/.shrc avec le contenu suivant, note ksh lit é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

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 ce faire, créez le fichier exécutable /usr/local/bin/**depth** (il devrait donc être accessible à l'ensemble du système) :

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

Modifiez le fichier avec votre éditeur préféré et ajoutez les lignes suivantes à son 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 -v o --verbose qui produira une liste des shells concernés. Et une autre option qui vérifiera si la profondeur est supérieure à un et qui, en fonction de cela, renverra exit 0 o exit 1 Vous pouvez donc l'utiliser de la manière suivante 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 avec les autres solutions : J'ai passé un peu plus de temps à découvrir les faiblesses des approches proposées ici. J'ai pu imaginer les deux cas suivants (les lettres majuscules sont nécessaires pour une meilleure mise en évidence de la syntaxe) :

  • Quand su o sudo -i sont concerné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
  • Un processus d'arrière-plan est alors 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
    DETH: 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

31voto

egmont Points 7095

Vérifier la valeur du SHLVL Shell :

echo $SHLVL

Citation de bash page du manuel :

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

Il est également soutenu par zsh .

9voto

waltinator Points 32821

En mi .bashrc J'utilise $SHLVL pour ajuster $PS1 , en ajoutant " + Les signes " à mon $SUBSHELL variable :

...
# 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
...

Je peux alors voir à quel point je suis profond :

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)$

4voto

bac0n Points 4841

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 le 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}"

2voto

aragaer Points 121

Vous pouvez simplement utiliser ps sans aucun argument supplémentaire pour voir toute la pile Shell (y compris la pile actuelle). Il affichera également toutes les tâches d'arrière-plan que vous avez lancées, ainsi que les éléments suivants ps mais il peut vous donner une idée approximative de la profondeur à laquelle vous vous trouvez.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X