ฉันจะรู้ได้อย่างไรว่าฉันมี sub-shells ลึกเพียงใด


40

:shบางครั้งผมทำสิ่งต่างๆเช่นการเริ่มต้นย่อยเปลือกจากกลุ่มด้วย ฉันจะรู้ได้อย่างไรว่าฉันอยู่ใน sub-shell ซึ่งexitจะให้ฉันกลับมาหนึ่งระดับหรือไม่และอยู่ใน shell นอกสุดที่exitจะออกจากระบบหรือปิดเซสชันของฉัน

มี Totem Inception ประเภทใดบ้างที่ฉันสามารถหมุนหรืออะไรบางอย่างที่จะรู้ว่าฉันอยู่ลึกแค่ไหน?



1
Hi! วิธีหนึ่งที่รวดเร็วในการดูว่าคุณอยู่ใน subshell echo $0หรือไม่คือการ ถ้ามันเป็นเปลือกหอยระดับบนมันอาจจะเริ่มต้นด้วยเส้นประ (นี่เป็นจริงอย่างน้อยสำหรับทุบตีและเส้นประหมายความว่ามันเป็นชื่อล็อกอินเชลล์)
jpaugh

คำตอบ:


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

อ้างอิง:


เพิ่มตัวบ่งชี้ไปยังพรอมต์ของเชลล์:ตามแนวคิดของ @ waltinator เพื่อให้มีตัวนับที่ด้านหน้าของพรอมต์สำหรับเชลล์ที่แตกต่างกันหลายอันเมื่อระดับนั้นลึกกว่าหนึ่งฉันได้เพิ่มบรรทัดดังที่แสดงด้านล่างของการสาธิต ที่ด้านล่างของไฟล์คำสั่ง run ( ~/.*rc) ที่เกี่ยวข้อง

ฉันทำการทดสอบบน WSL, Ubuntu 16.04, Ubuntu 18.04 (เซิร์ฟเวอร์ / เดสก์ท็อป), Ubuntu 19.04, ภายใน gnome-terminal, เซสชัน tty และ ssh นี่คือวิธีการทำงาน:

ป้อนคำอธิบายรูปภาพที่นี่

ข้อ จำกัด คือตัวนับทำงานได้เฉพาะระดับความลึก 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
  • cshและ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
  • 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

อ้างอิง:


สร้างคำสั่งที่จะส่งออกความลึก:อีกตัวเลือกหนึ่งคือการสร้างคำสั่งเชลล์ที่จะส่งออกความลึก เพื่อจุดประสงค์นี้สร้างไฟล์เรียกทำงาน(ดังนั้นควรเป็นระบบที่เข้าถึงได้กว้าง):/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จะส่งรายการของเชลล์ที่เกี่ยวข้อง และอีกตัวเลือกหนึ่งที่จะตรวจสอบว่าความลึกมากกว่าหนึ่งและขึ้นอยู่กับสิ่งนี้จะกลับมาexit 0หรือ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

เปรียบเทียบกับโซลูชันอื่น ๆ :ฉันใช้เวลาเพิ่มในการหาจุดอ่อนของแนวทางที่ให้ไว้ที่นี่ ฉันสามารถจินตนาการถึงสองกรณีต่อไปนี้ (จำเป็นต้องใช้อักษรตัวใหญ่สำหรับการเน้นไวยากรณ์ที่ดีกว่า):

  • เมื่อไหร่suหรือsudo -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. ทำไมเป็นอย่างนี้
Val

1
@val: systemd เป็นกระบวนการเริ่มต้นซึ่งเป็นแม่ของกระบวนการอื่น ๆ ทั้งหมด เห็นได้ชัดว่าคุณกำลังใช้xfce4-terminalซึ่งเปิดตัวbashเชลล์ภายในซึ่งคุณวิ่งpstreeซึ่งรายงานตัวเองและผู้ปกครอง หากคุณหมายถึงการขาดขั้นตอนระหว่าง systemd และ xfce4-terminal อาจเป็นไปได้ว่าสิ่งที่เปิดตัว xfce4-terminal ตายหรือปฏิเสธมันในกรณีนี้มันจะได้รับมรดกโดย init
Nick Matteo

ด้วยเหตุผลใดที่จะไม่อ่านSHLVL? ความสะดวกในการพกพาข้ามกระบวนการและระบบฉันคิดว่า แต่ pstree อาจไม่ได้รับการติดตั้ง ..
D. Ben Knoble

สวัสดี @ D.BenKnoble เป็นที่กล่าวถึงภายใต้ของ @ Egmont คำตอบ , $SHLVLไม่ได้รับการสนับสนุนจากเปลือกบาง เฉพาะเจาะจงมากขึ้นตามสภาพแวดล้อมจากการสาธิตข้างต้นมันไม่ได้รับการสนับสนุนโดยsh( dash) - และเชลล์นี้ไม่ได้ถูกนับโดยตัวแปรนี้ บนมืออื่น ๆpstreeเป็นส่วนหนึ่งของแพคเกจpsmiscที่ให้ยังfuser, killallและอื่น ๆ น้อย - มันเป็นองค์ประกอบหลักของอูบุนตู - ฉันไม่ได้ติดตั้งบนระบบที่กล่าวถึงในคำตอบนี้
pa4080

30

ตรวจสอบค่าของSHLVLตัวแปรเชลล์:

echo $SHLVL

การอ้างอิงจากbashหน้าคู่มือของ:

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

zshนอกจากนี้ยังได้รับการสนับสนุนโดย


4
แต่จะไม่นับ sh ดังนั้นตัวอย่างที่ให้กับ sh จะไม่เพิ่มค่า SHLVL ถึงกระนั้นนี่เป็นสิ่งที่อาจเป็นประโยชน์สำหรับผู้ที่ไม่ได้เปลี่ยนเชลล์มากเกินไป
ubfan 1

3
@ ubfan1 เว้นแต่ว่าจะมีคำจำกัดความ vimrc ที่เหนือกว่า:shค่าเริ่มต้นไปยังเปลือกเข้าสู่ระบบของผู้ใช้ฉันคิดว่า (มันเป็นรูปแบบย่อของ:shellแทนที่จะเป็นชื่อของไบนารีเชลล์ที่เฉพาะเจาะจง)
steeldriver

3
ฉันไม่คุ้นเคยกับรายละเอียดที่เป็นกลุ่ม แต่ฉันได้พยายามออก:shจากvimก่อนโพสต์คำตอบนี้และมันก็เพิ่มระดับเปลือกสำหรับฉัน เปลือกเข้าสู่ระบบของฉันเป็นทุบตี
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โดยไม่มีอาร์กิวเมนต์เพิ่มเติมเพื่อดูเชลล์สแต็กทั้งหมด (รวมถึงปัจจุบัน) มันจะแสดงงานพื้นหลังทั้งหมดที่คุณเริ่มต้นและpsตัวของมันเองด้วยเช่นกันแต่มันจะช่วยให้คุณประมาณการคร่าวๆว่าคุณอยู่ลึกแค่ไหน


วิธีนี้ใช้{ echo hello world; ps; } &เพื่อพิสูจน์psคำตอบข้างต้น
WinEunuuchs2Unix

@ WinEunuuchs2Unix ฉันหมายถึงสิ่งนี้: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080

มีวิธีเลียนแบบ pstree -s $$ กับ ps หรือไม่?
bac0n
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.