我怎么知道我有多少个子壳?


40

有时我会做一些事情,例如使用vi从vim启动一个子shell :sh。我怎么知道我是否在子外壳exit中将我退出一个级别,而在最外壳exit中将退出我或关闭会话。

我可以旋转某种盗版图腾,还是可以知道我有多少层深度的东西?


5
有关vi.stackexchange.com的信息:我怎么知道我在vi命令:sh的外壳中?
steeldriver

1
嗨!一种快速查看是否在子外壳中的方法是echo $0。如果是顶层外壳,则可能以破折号开头。(至少对于bash来说是这样,短划线表示它是所谓的登录shell。)
jpaugh

Answers:


40

您可以使用命令pstree(Ubuntu默认随附)。这是示例-当前我在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}

在实际的Linux / Ubuntu环境中,过程树将更加复杂。我们可以通过-s显示所选过程的父项的选项来过滤树。因此我们的命令可能是pstree -s $$,其中$$是包含当前PID的环境变量:

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

参考文献:


在shell的提示符中添加指示器:基于@waltinator的想法,为了在级别超过1时在提示符前面有一个计数器,用于存放多个不同的shell,我添加了以下示例中的行,在相关运行命令~/.*rc)文件的底部。

我已经在gnome-terminal,tty和ssh会话中对WSL,Ubuntu 16.04,Ubuntu 18.04(服务器/桌面),Ubuntu 19.04进行了测试。这是这样的:

在此处输入图片说明

限制是:计数器仅适用于13至14级深度,具体取决于操作系统。我无意调查原因:)

  • 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
    
  • cshtcsh> .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实际上是dash在Ubuntu上-这里的事情有点复杂和连线(请阅读下面的参考资料以获得更多信息):

    1. 编辑~/.profile文件,并在底部添加以下行:

      ENV=$HOME/.shrc; export ENV
    2. 创建~/.shrc具有以下内容的文件,注意ksh也将读取$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
      

参考文献:


创建一个将输出深度的命令:另一个选项是创建将输出深度的shell命令。为此,请创建可执行文件(因此应在系统范围内进行访问):/usr/local/bin/depth

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

使用您喜欢的编辑器编辑文件,并添加以下几行作为内容:

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

上面的脚本有两个选项,-v或者--verbose将输出涉及的shell的列表。而另一个将检查深度是否大于一个并基于此返回的选项将返回exit 0or exit 1,因此您可以以这种方式使用它depth && exit。以下是一些用法示例:

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

与其他解决方案的比较:我花了一些额外的时间来找出此处提供的方法的一些缺点。我能够想象以下两种情况(为了更好地突出显示语法,需要使用大写字母):

  • 何时susudo -i涉及:

    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
    
  • 启动后台进程后:

    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
    

现在,我对系统上的输出感到困惑:systemd───xfce4-terminal───bash───pstree。为什么这样呢?
瓦尔

1
@val:systemd是init进程,是所有其他进程的父级。显然您正在使用xfce4-terminal,它启动了一个bash外壳程序,您在其中运行了该外壳程序,该外壳程序pstree报告了其自身及其父级。如果您的意思是systemd和xfce4-terminal之间没有步骤,则可能是因为启动的xfce4-terminal死亡或被放弃,在这种情况下,它将被init继承。
尼克·马特奥

有什么不读书的理由SHLVL吗?整个流程和系统的兼容性问题,我想,但后来pstree可能没有安装..
D.本Knoble

你好,@ D.BenKnoble,因为它是在@埃格蒙特的下讨论答案$SHLVL不被一些炮弹支持。更具体地说,根据上面演示中的环境,它不仅仅受shdash)的支持-此变量根本不计入此shell。另一方面pstreepsmisc软件包的一部分,该软件包还提供psmiscfuserkillall而其他软件包很少-它是Ubuntu的主要组件-我尚未在此答案中提到的系统上安装它。
pa4080

30

检查SHLVLshell变量的值:

echo $SHLVL

bash的手册页引用:

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

也受到的支持zsh


4
但是不计算sh,因此使用sh给出的示例不会增加SHLVL。尽管如此,这对于那些没有过多切换shell的人可能是有用的
ubfan1

3
@ ubfan1,除非有覆盖的vimrc定义,否则:sh默认为我认为的用户登录外壳程序(它实际上是缩写形式,:shell而不是特定外壳程序二进制文件的名称)
steeldriver

3
我不熟悉的Vim的细节,但我已经尝试了:shvim发布这个答案之前,它确实增加了外壳的水平我。我的登录shell是bash。
egmont

9

在我的代码中.bashrc,我常通过在变量中添加“ ”符号来$SHLVL进行调整: $PS1+$SUBSHELL

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

然后,我可以看到我有多深:

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

您可以将两个版本之一放在文件中,并使用源使$ 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

您可以简单地使用ps而无需任何其他参数来查看整个shell堆栈(包括当前的堆栈)。它还将显示您已开始的所有后台作业及其ps本身,但可以粗略估计您的工作深度。


这可以{ echo hello world; ps; } &证明上述ps答案。
WinEunuuchs2Unix

@ WinEunuuchs2Unix,我的意思是这样的:paste.ubuntu.com/p/6Kfg8TqR9V
pa4080

有没有办法用ps模仿pstree -s $$?
bac0n
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.