Woher weiß ich, wie viele Unterschalen tief ich bin?

40

Manchmal mache ich Dinge wie das Starten einer Sub-Shell von vim mit :sh. Woher weiß ich, ob ich mich in einer Sub-Shell exitbefinde, in der ich nur eine Ebene zurückgebe, oder in der äußersten Shell, in der exitich mich abmelden oder meine Sitzung schließen werde?

Gibt es eine Art Inception-Totem, das ich drehen kann, oder etwas, um zu wissen, wie viele Ebenen ich tief bin?

Wyck
quelle
5
Related at vi.stackexchange.com: Woher weiß ich, dass ich mich in einer Shell des Befehls vi
Steeldriver
1
Hallo! Ein schneller Weg, um zu sehen, ob Sie in einer Unterschale sind oder nicht, ist zu echo $0. Wenn es sich um die oberste Ebene handelt, beginnt sie wahrscheinlich mit einem Gedankenstrich. (Dies gilt zumindest für Bash, und der Bindestrich bedeutet, dass es sich um eine sogenannte Login-Shell handelt.)
30.

Antworten:

40

Sie können den Befehl verwenden pstree(der standardmäßig in Ubuntu enthalten ist). Hier ist ein Beispiel - zur Zeit habe ich nur ein offenes Terminalfenster in der 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}

In einer tatsächlichen Linux / Ubuntu-Umgebung wird der Prozessbaum komplizierter. Wir können den Baum nach der Option filtern, die -sdie Eltern eines ausgewählten Prozesses anzeigt. Unser Befehl könnte also lauten pstree -s $$: Wo $$ist eine Umgebungsvariable, die die aktuelle PID enthält?

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

Verweise:


Hinzufügen eines Indikators zur Shell-Eingabeaufforderung: Basierend auf der Idee des @ Waltinators habe ich die unter der Demo gezeigten Zeilen hinzugefügt, um einen Zähler für mehrere verschiedene Shells vor der Eingabeaufforderung zu haben, wenn das Level tiefer als eins ist. am unteren Rand der entsprechenden run-Anweisungen ( ~/.*rc) -Dateien.

Ich habe Tests auf WSL, Ubuntu 16.04, Ubuntu 18.04 (Server / Desktop), Ubuntu 19.04 innerhalb von Gnome-Terminal, Tty und SSH-Sitzung gemacht. So funktioniert das:

Bildbeschreibung hier eingeben

Die Einschränkung besteht darin, dass der Zähler je nach Betriebssystem nur für 13-14 Tiefenstufen funktioniert. Ich habe nicht vor, die Gründe zu untersuchen :)

  • 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
  • cshund 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
  • shdas ist eigentlich dashauf Ubuntu - hier sind die Dinge etwas kompliziert und verkabelt (lesen Sie die Referenzen unten für weitere Informationen):

    1. Bearbeiten Sie die ~/.profileDatei und fügen Sie die folgende Zeile unten hinzu:

      ENV=$HOME/.shrc; export ENV
    2. Erstellen Sie die Datei ~/.shrcmit dem folgenden Inhalt, Hinweis kshliest auch die $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

Verweise:


Erstellen Sie einen Befehl, der die Tiefe ausgibt: Eine andere Option besteht darin, einen Shell-Befehl zu erstellen, der die Tiefe ausgibt. Zu diesem Zweck erstellen Sie die ausführbare Datei (daher sollte sie systemweit zugänglich sein):/usr/local/bin/depth

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

Bearbeiten Sie die Datei mit Ihrem bevorzugten Editor und fügen Sie die folgenden Zeilen als Inhalt hinzu:

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

Das obige Skript hat zwei Optionen -voder --verbosees wird eine Liste der beteiligten Shells ausgegeben. Und die andere Option, die prüft, ob die Tiefe größer als eins ist und darauf basiert, gibt exit 0oder zurück exit 1, sodass Sie sie auf diese Weise verwenden können depth && exit. Hier einige Anwendungsbeispiele:

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

Vergleich mit den anderen Lösungen: Ich habe zusätzliche Zeit aufgewendet, um einige Schwächen der hier vorgestellten Ansätze herauszufinden. Ich konnte mir die folgenden zwei Fälle vorstellen (die Großbuchstaben werden für eine bessere Hervorhebung der Syntax benötigt):

  • Wann suoder sudo -isind beteiligt:

    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
  • Wenn dort ein Hintergrundprozess gestartet wird:

    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
pa4080
quelle
Jetzt verwirrt ich über Ausgabe ich auf meinem System bekommen: systemd───xfce4-terminal───bash───pstree. Warum ist es so?
val
1
@val: systemd ist der init-Prozess, der übergeordnete aller anderen Prozesse. Sie verwenden anscheinend xfce4-terminaleine bashShell, in der Sie gelaufen sind pstree, die sich und ihre Eltern gemeldet hat. Wenn Sie das Fehlen von Schritten zwischen systemd und xfce4-terminal meinen, könnte es sein, dass das gestartete xfce4-terminal abgestürzt ist oder es nicht mehr gehört. In diesem Fall würde es von init geerbt.
Nick Matteo
Irgendein Grund, nicht zu lesen SHLVL? Portabilität über Prozesse und Systeme, nehme ich an, aber dann kann pstree nicht installiert werden.
D. Ben Knoble
Hallo, @ D.BenKnoble, wie unter der Antwort von @ egmont beschrieben , $SHLVLwird von einigen Shells nicht unterstützt. Genauer gesagt, je nach Umgebung aus der obigen Demo wird es nicht nur von sh( dash) unterstützt - und diese Shell wird von dieser Variablen überhaupt nicht gezählt. Auf der anderen Seite pstreeist Teil des Pakets psmisc , die auch bietet fuser, killallund einige andere - es Hauptbestandteil von Ubuntu ist - ich habe es installiert nicht auf die Systeme in dieser Antwort erwähnt.
pa4080
30

Überprüfen Sie den Wert der SHLVLShell-Variablen:

echo $SHLVL

Zitat aus bashder Handbuchseite:

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

Es wird auch von unterstützt zsh.

egmont
quelle
4
Da sh jedoch nicht gezählt wird, hätte das angegebene Beispiel mit sh SHLVL nicht erhöht. Dennoch ist dies etwas, das für diejenigen nützlich sein könnte, die nicht zu viel Muscheln wechseln
ubfan1
3
@ ubfan1 Wenn es keine übergeordnete vimrc-Definition gibt, wird :shstandardmäßig die Anmeldeshell des Benutzers verwendet (es ist eigentlich eher eine abgekürzte Form :shellals der Name einer bestimmten Shell-Binärdatei)
steeldriver
3
Ich bin mit vim Details nicht vertraut, aber ich habe versucht , aus :shaus , vimbevor dieser Antwort veröffentlichen, und es hat für mich die Shell - Ebene Zuwachs. Meine Login-Shell ist Bash.
Montag,
9

In my passe .bashrcich $SHLVLan $PS1, indem ich " +" Zeichen an meine $SUBSHELLVariable anhänge:

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

Dann kann ich sehen, wie tief ich bin:

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

Sie können eine der beiden Versionen in eine Datei einfügen und mit source $ DEPTH verfügbar machen.

# 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}"
bac0n
quelle
2

Sie können einfach psohne zusätzliche Argumente den gesamten Shell-Stack (einschließlich des aktuellen) anzeigen. Es werden auch alle von Ihnen gestarteten Hintergrundjobs sowie sich psselbst angezeigt, aber es kann Ihnen eine ungefähre Einschätzung darüber geben, wie tief Sie sind.

Aragaer
quelle
Dies funktioniert { echo hello world; ps; } &, um die psAntwort oben zu beweisen .
WinEunuuchs2Unix
@ WinEunuuchs2Unix, ich meine so etwas wie: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080
Gibt es eine Möglichkeit, pstree -s $$ mit ps zu imitieren?
1.