¿Cómo sé cuántos subcapas de profundidad tengo?


40

A veces hago cosas como comenzar un sub-shell desde vim con :sh. ¿Cómo sé si estoy en un sub-shell donde exitsolo me devolverá un nivel, en lugar de estar en el shell más externo donde exitcerraré la sesión o cerraré mi sesión?

¿Hay algún tipo de tótem de inicio que pueda girar o algo para saber cuántos niveles tengo?



1
¡Hola! Una forma rápida de ver si estás en una subshell o no es hacerlo echo $0. Si es el shell de nivel superior, probablemente comenzará con un guión. (Esto es cierto al menos para bash, y el guión significa que es un llamado shell de inicio de sesión.)
jpaugh

Respuestas:


40

Puede usar el comando pstree(que viene por defecto con Ubuntu). Aquí hay un ejemplo: actualmente solo tengo una ventana de terminal abierta en 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}

Dentro de un entorno real de Linux / Ubuntu, el árbol de procesos será más complicado. Podemos filtrar el árbol por la opción -sque mostrará a los padres de un proceso seleccionado. Entonces, nuestro comando podría ser pstree -s $$, donde $$es una variable de entorno que contiene el PID actual:

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

Referencias


Agregue un indicador a la solicitud del shell: según la idea de @ waltinator , para tener un contador al frente de la solicitud para varios shells diferentes cuando el nivel es más profundo que uno, he agregado las líneas, que se muestran debajo de la demostración, en la parte inferior de los archivos de comandos de ejecución ( ~/.*rc) relevantes .

He realizado pruebas en WSL, Ubuntu 16.04, Ubuntu 18.04 (servidor / escritorio), Ubuntu 19.04, dentro de la sesión gnome-terminal, tty y ssh. Así es como funciona:

ingrese la descripción de la imagen aquí

La limitación es que: el contador funciona solo para 13-14 niveles de profundidad, dependiendo del sistema operativo. No tengo la intención de investigar las razones :)

  • 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
  • cshy 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
  • sheso está realmente dashen Ubuntu: aquí las cosas son un poco complicadas y están cableadas (lea las referencias a continuación para obtener más información):

    1. Edite el ~/.profilearchivo y agregue la siguiente línea en la parte inferior:

      ENV=$HOME/.shrc; export ENV
    2. Cree el archivo ~/.shrccon el siguiente contenido, la nota kshtambién lee el $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

Referencias


Cree un comando que genere la profundidad: otra opción es crear un comando de shell que genere la profundidad. Para este propósito, cree el archivo ejecutable (por lo tanto, debe ser accesible en todo el sistema):/usr/local/bin/depth

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

Edite el archivo con su editor favorito y agregue las siguientes líneas como su contenido:

#!/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

El script anterior tiene dos opciones -vo --verboseque generará una lista de los shells involucrados. Y la otra opción que verificará si la profundidad es mayor que una y, en función de esto, regresará exit 0o exit 1, para que pueda usarla de esta manera depth && exit. Aquí hay algunos ejemplos de uso:

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

Comparación con las otras soluciones: pasé un tiempo adicional para descubrir algunas debilidades de los enfoques proporcionados aquí. Pude imaginar los siguientes dos casos (las mayúsculas son necesarias para un mejor resaltado de sintaxis):

  • Cuando suo sudo -iestán involucrados:

    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
  • Cuando se inicia un proceso en segundo plano:

    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

Ahora estoy confundido acerca de la producción llegué en mi sistema: systemd───xfce4-terminal───bash───pstree. ¿Por qué es así?
Val dice reinstalar a Mónica el

1
@val: systemd es el proceso de inicio, el padre de todos los demás procesos. Aparentemente estás usando xfce4-terminal, que lanzó un bashshell, dentro del cual corriste pstree, que se informó a sí mismo y a sus padres. Si te refieres a la falta de pasos entre systemd y xfce4-terminal, podría ser que lo que sea que se haya lanzado xfce4-terminal lo haya muerto o no, en cuyo caso sería heredado por init.
Nick Matteo

¿Alguna razón para no leer SHLVL? Supongo que la portabilidad a través de procesos y sistemas, pero luego pstree no puede instalarse ..
D. Ben Knoble

Hola, @ D.BenKnoble, como se analiza en la respuesta de @ egmont , $SHLVLno es compatible con algunos shells. Más específicamente, de acuerdo con el entorno de la demostración anterior, no solo es compatible con sh( dash), y esta shell no cuenta en absoluto esta shell. Por otro lado, pstreees parte del paquete psmisc que también proporciona fuser, killally algunos otros, es el componente principal de Ubuntu, no lo he instalado en los sistemas mencionados en esta respuesta.
pa4080

30

Verifique el valor de la SHLVLvariable de shell:

echo $SHLVL

Citando de bashla página del manual:

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

También es apoyado por zsh .


44
Pero sh no se cuenta, por lo que el ejemplo dado, con sh, no habría incrementado SHLVL. Aún así, esto es algo que podría ser útil para aquellos que no cambian demasiado los shells
ubfan1

3
@ ubfan1, a menos que haya una definición de vimrc sobresaliente, :shcreo que el valor predeterminado es el shell de inicio de sesión del usuario (en realidad es una forma abreviada en :shelllugar del nombre de un binario de shell específico)
steeldriver

3
No estoy familiarizado con los detalles del vim, pero yo he probado a cabo :shdesde vimantes de la publicación de esta respuesta, y lo hizo el nivel mínimo de la cáscara para mí. Mi shell de inicio de sesión es bash.
egmont

9

En mi .bashrc, solía $SHLVLajustar $PS1, agregando " +" signos a mi$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
...

Entonces, puedo ver lo profundo que soy:

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

Puede colocar una de las dos versiones en un archivo y usar la fuente para hacer que $ DEPTH esté 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

Simplemente puede usar pssin ningún argumento adicional para ver toda la pila de shell (incluida la actual). También mostrará todos los trabajos en segundo plano que ha comenzado, así como a pssí mismo, pero puede darle una estimación aproximada de cuán profundo es.


Esto funciona { echo hello world; ps; } &para probar la psrespuesta anterior.
WinEunuuchs2Unix

@ WinEunuuchs2Unix, quiero decir algo como esto: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080

¿Hay alguna manera de imitar pstree -s $$ con ps?
Bac0n
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.