Come faccio a sapere quante sub-shell sono profonde?


40

A volte faccio cose come l'avvio di una sub-shell da VIM con :sh. Come faccio a sapere se mi trovo in una sotto-shell in cui exitmi farà tornare indietro di un livello, invece di trovarmi nella shell più esterna in cui exitmi disconnetterò o chiuderò la sessione.

C'è una sorta di totem di Inception che posso girare o qualcosa per sapere quanti livelli sono profondi?



1
Ciao! Un modo rapido per vedere se sei in una subshell o no è farlo echo $0. Se è la shell di livello superiore, probabilmente inizierà con un trattino. (Questo è vero almeno per bash, e il trattino significa che è una cosiddetta shell di login.)
jpaugh

Risposte:


40

Puoi usare il comando pstree(che viene fornito di default con Ubuntu). Ecco un esempio: attualmente ho solo una finestra terminale aperta su 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}

All'interno di un vero ambiente Linux / Ubuntu l'albero dei processi sarà più complicato. Possiamo filtrare l'albero con l'opzione -sche mostrerà i genitori di un processo selezionato. Quindi potrebbe essere il nostro comando pstree -s $$, dov'è $$una variabile di ambiente che contiene il PID corrente:

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

Riferimenti:


Aggiungere indicatore del prompt della shell: Sulla base del @ del waltinator idea , al fine di avere un contatore nella parte anteriore del prompt per diverse shell differenti quando il livello è più profondo di quello, ho aggiunto le righe, riportate di seguito la demo, nella parte inferiore dei relativi file comandi ( ~/.*rc).

Ho fatto test su WSL, Ubuntu 16.04, Ubuntu 18.04 (server / desktop), Ubuntu 19.04, all'interno della sessione gnome-terminal, tty e ssh. Ecco come funziona:

inserisci qui la descrizione dell'immagine

Il limite è che: il contatore funziona solo per 13-14 livelli di profondità, a seconda del sistema operativo. Non intendo indagare sui motivi :)

  • 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
  • cshe 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
  • shche in realtà è dashsu Ubuntu - qui le cose sono un po 'complicate e cablate (leggi i riferimenti sotto per maggiori informazioni):

    1. Modifica il ~/.profilefile e aggiungi la seguente riga in fondo:

      ENV=$HOME/.shrc; export ENV
    2. Crea il file ~/.shrccon il seguente contenuto, nota kshanche $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

Riferimenti:


Creare un comando che genererà la profondità: Un'altra opzione è quella di creare un comando shell che produrrà la profondità. A tal fine creare il file eseguibile (quindi dovrebbe essere accessibile a livello di sistema):/usr/local/bin/depth

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

Modifica il file con il tuo editor preferito e aggiungi le seguenti righe come contenuto:

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

Lo script sopra ha due opzioni -vo --verboseche mostrerà un elenco delle shell coinvolte. E un'altra opzione che controllerà se la profondità è maggiore di una e in base a ciò tornerà exit 0o exit 1, quindi puoi usarla in questo modo depth && exit. Ecco alcuni esempi di utilizzo:

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

Confronto con le altre soluzioni: ho trascorso del tempo aggiuntivo per scoprire alcuni punti deboli degli approcci forniti qui. Sono stato in grado di immaginare i seguenti due casi (le lettere maiuscole sono necessarie per una migliore evidenziazione della sintassi):

  • Quando suo sudo -isono coinvolti:

    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
  • Quando viene avviato un processo in background:

    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

Ora sto perplesso su uscita ho avuto sul mio sistema: systemd───xfce4-terminal───bash───pstree. Perché è in questo modo?
dice Val Reinstate Monica il

1
@val: systemd è il processo init, il genitore di tutti gli altri processi. Apparentemente stai usando xfce4-terminal, che ha lanciato una bashshell, all'interno della quale corri pstree, che riportava se stessa e i suoi genitori. Se intendi la mancanza di passaggi tra systemd e xfce4-terminal, potrebbe essere che qualsiasi cosa lanciata xfce4-terminal sia morta o rinnegata, nel qual caso sarebbe ereditata da init.
Nick Matteo

Qualche motivo per non leggere SHLVL? Portabilità attraverso processi e sistemi, presumo, ma poi pstree potrebbe non essere installato ..
D. Ben Knoble

Ciao, @ D.BenKnoble, come è discusso nella risposta di @ egmont , $SHLVLnon è supportato da alcune shell. Più specifico, secondo l'ambiente della demo sopra non è supportato solo da sh( dash) - e questa shell non è affatto considerata da questa variabile. D'altra parte pstreefa parte del pacchetto psmisc che fornisce anche fuser, killalle pochi altri - è il componente principale di Ubuntu - non l'ho installato sui sistemi menzionati in questa risposta.
pa4080,

30

Verifica il valore della SHLVLvariabile shell:

echo $SHLVL

Citando dalla bashpagina del manuale:

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

È anche supportato da zsh.


4
Ma sh non viene conteggiato, quindi l'esempio fornito, con sh, non avrebbe incrementato SHLVL. Tuttavia, questo è qualcosa che potrebbe essere utile per coloro che non
cambiano

3
@ ubfan1 a meno che non ci sia una definizione prevalente di vimrc, per :shimpostazione predefinita penso alla shell di login dell'utente (è davvero una forma abbreviata :shellpiuttosto che il nome di un binario della shell specifico)
steeldriver

3
Non ho dimestichezza con i dettagli di Vim, ma ho provato :shda vimprima di pubblicare questa risposta, e lo ha fatto di incremento del livello di shell per me. La mia shell di accesso è bash.
egmont,

9

Nel mio .bashrc, uso $SHLVLper regolare $PS1, aggiungendo " +" segni alla mia $SUBSHELLvariabile:

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

Quindi, posso vedere quanto sono profondo:

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

È possibile inserire una delle due versioni in un file e utilizzare source per rendere disponibile $ DEPTH.

# 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

Puoi semplicemente usare pssenza ulteriori argomenti per vedere l'intero stack di shell (incluso quello corrente). Mostrerà anche tutti i lavori in background che hai iniziato così come psse stesso, ma può darti una stima approssimativa della tua profondità.


Questo funziona { echo hello world; ps; } &per dimostrare la psrisposta sopra.
WinEunuuchs2Unix

@ WinEunuuchs2Unix, intendo qualcosa del genere: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080

C'è un modo per imitare pstree -s $$ con ps?
bac0n
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.