Como sei quantas subconchas tenho profundidade?

40

Às vezes, faço coisas como iniciar um sub-shell do vim :sh. Como sei se estou em um sub-shell, onde exitapenas me expulsará um nível, versus estar no shell mais externo, onde exitme desconectarei ou encerrarei minha sessão.

Existe algum tipo de totem de iniciação que eu possa girar ou algo para saber quantos níveis de profundidade eu tenho?

Wyck
fonte
5
Relacionado em vi.stackexchange.com: Como sei que estou em um shell no comando vi: sh?
steeldriver 27/09
11
Oi! Uma maneira rápida de ver se você está em um subshell ou não é echo $0. Se for o shell de nível superior, provavelmente começará com um traço. (Isso é verdade pelo menos para o bash, e o traço significa que é um chamado shell de login.)
jpaugh 30/09

Respostas:

40

Você pode usar o comando pstree(que vem por padrão com o Ubuntu). Aqui está um exemplo - atualmente, estou tendo apenas uma janela de terminal aberta na 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 um ambiente Linux / Ubuntu real, a árvore de processos será mais complicada. Podemos filtrar a árvore pela opção -sque mostrará os pais de um processo selecionado. Portanto, nosso comando poderia ser pstree -s $$, onde $$está uma variável de ambiente que contém o PID atual:

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

Referências:


Adicionar indicador ao prompt do shell: com base na idéia do @ waltinator , para ter um contador na frente do prompt para vários shells diferentes quando o nível for mais profundo do que um, adicionei as linhas, mostradas abaixo da demonstração, na parte inferior dos arquivos de comandos de execução relevantes ( ~/.*rc).

Fiz testes na WSL, Ubuntu 16.04, Ubuntu 18.04 (servidor / desktop), Ubuntu 19.04, na sessão gnome-terminal, tty e ssh. Aqui está como isso funciona:

insira a descrição da imagem aqui

A limitação é a seguinte: o contador funciona apenas para 13 a 14 níveis de profundidade, dependendo do sistema operacional. Não pretendo investigar os motivos :)

  • 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
    
  • shque está realmente dashno Ubuntu - aqui as coisas são um pouco complicadas e cabeadas (leia as referências abaixo para obter mais informações):

    1. Edite o ~/.profilearquivo e adicione a seguinte linha na parte inferior:

      ENV=$HOME/.shrc; export ENV
    2. Crie o arquivo ~/.shrccom o seguinte conteúdo, observe kshtambém o seguinte $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
      

Referências:


Crie um comando que produzirá a profundidade: Outra opção é criar um comando shell que produzirá a profundidade. Para esse fim, crie o arquivo executável (portanto, ele deve estar acessível em todo o sistema):/usr/local/bin/depth

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

Edite o arquivo com seu editor favorito e adicione as seguintes linhas como conteúdo:

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

O script acima tem duas opções -vou --verboseque produzirão uma lista dos shells envolvidos. E a outra opção que verificará se a profundidade é maior que uma e com base nisso retornará exit 0ou exit 1, para que você possa usá-la dessa maneira depth && exit. Aqui estão alguns exemplos 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

Comparação por outras soluções: Passei algum tempo adicional para descobrir algumas fraquezas das abordagens fornecidas aqui. Consegui imaginar os dois casos a seguir (as letras maiúsculas são necessárias para melhor destaque da sintaxe):

  • Quando suou sudo -iestá envolvido:

    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 um processo em segundo plano é iniciado:

    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
fonte
Agora estou intrigado sobre a saída que eu tenho no meu sistema: systemd───xfce4-terminal───bash───pstree. Porque é este caminho?
val
11
@val: systemd é o processo init, o pai de todos os outros processos. Aparentemente xfce4-terminal, você está usando , o que lançou um bashshell, no qual você executou pstree, que relatou a si mesmo e a seus pais. Se você quer dizer a falta de etapas entre o systemd e o xfce4-terminal, pode ser que qualquer que seja o xfce4-terminal lançado tenha morrido ou o deserdado, caso em que seria herdado pelo init.
Nick Matteo
Alguma razão para não ler SHLVL? Portabilidade entre processos e sistemas, suponho, mas o pstree pode não estar instalado.
D. Ben Knoble 29/09
Olá, @ D.BenKnoble, como é discutido na resposta do @ egmont , $SHLVLnão é suportado por alguns shells. Mais específico, de acordo com o ambiente da demonstração acima, ele não é suportado apenas por sh( dash) - e esse shell não é contabilizado de maneira alguma por essa variável. Por outro lado, pstreefaz parte do pacote psmisc que também fornece fuser, killalle poucos outros - é o principal componente do Ubuntu - não o instalei nos sistemas mencionados nesta resposta.
pa4080 30/09
30

Verifique o valor da SHLVLvariável do shell:

echo $SHLVL

Citando a partir bashda página de manual:

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

Também é suportado por zsh.

Egmont
fonte
4
Mas sh não é contado, portanto, o exemplo dado com sh não teria incrementado o SHLVL. Ainda assim, isso é algo que pode ser útil para aqueles que não
trocam
3
@ ubfan1, a menos que exista uma definição de vimrc superior, o :shpadrão é o shell de login do usuário, eu acho (é realmente uma forma abreviada de :shelle não o nome de um binário específico do shell)
steeldriver
3
Eu não estou familiarizado com detalhes do vim, mas eu tentei fora :shde vimantes de postar esta resposta, e ele fez incremento do nível de shell para mim. Meu shell de login é bash.
egmont 28/09
9

No meu .bashrc, eu costumo $SHLVLajustar $PS1, acrescentando +sinais " " à minha $SUBSHELLvariável:

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

Então, eu posso ver o quão profundo eu sou:

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

Você pode colocar uma das duas versões em um arquivo e usar o source para disponibilizar o $ 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}"
bac0n
fonte
2

Você pode simplesmente usar, pssem argumentos adicionais, para ver a pilha inteira do shell (incluindo a atual). Ele também mostrará todos os trabalhos em segundo plano que você iniciou, assim como psele próprio, mas pode fornecer uma estimativa aproximada de quão profundo você é.

aragaer
fonte
Isso funciona { echo hello world; ps; } &para provar a psresposta acima.
WinEunuuchs2Unix 29/09
@ WinEunuuchs2Unix, quero dizer algo como isto: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080
Existe uma maneira de imitar pstree -s $$ com ps?
bac0n 01/10