Bagaimana saya tahu berapa banyak sub-kulit saya?

40

Terkadang saya melakukan hal-hal seperti memulai sub-shell dari vim with :sh. Bagaimana saya tahu jika saya berada di sub-shell di mana exithanya akan mengembalikan saya satu tingkat, vs berada di shell terluar di mana exitakan mengeluarkan saya atau menutup sesi saya.

Apakah ada semacam totem Inception yang dapat saya putar atau sesuatu untuk mengetahui berapa level saya?

Wyck
sumber
5
Terkait di vi.stackexchange.com: Bagaimana saya tahu saya di shell dari perintah vi: sh?
steeldriver
1
Hai! Salah satu cara cepat untuk melihat apakah Anda berada dalam subkulit atau tidak adalah dengan echo $0. Jika itu adalah shell tingkat atas, itu mungkin akan dimulai dengan tanda hubung. (Ini berlaku setidaknya untuk bash, dan tanda hubung berarti itu disebut shell login.)
jpaugh

Jawaban:

40

Anda dapat menggunakan perintah pstree(yang datang secara default dengan Ubuntu). Berikut ini contohnya - saat ini saya hanya memiliki satu jendela terminal terbuka di 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}

Dalam lingkungan Linux / Ubuntu yang sebenarnya pohon proses akan lebih rumit. Kami dapat memfilter pohon dengan opsi -syang akan menunjukkan kepada orang tua dari proses yang dipilih. Jadi perintah kami adalah pstree -s $$, di mana $$variabel lingkungan yang berisi PID saat ini:

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

Referensi:


Tambahkan indikator ke prompt shell: Berdasarkan ide @ waltinator , untuk memiliki penghitung di depan prompt untuk beberapa shell yang berbeda ketika levelnya lebih dalam dari satu, saya telah menambahkan garis, ditunjukkan di bawah demo, di bagian bawah file perintah jalankan yang relevan ( ~/.*rc).

Saya telah membuat tes pada WSL, Ubuntu 16.04, Ubuntu 18.04 (server / desktop), Ubuntu 19.04, dalam sesi gnome-terminal, tty dan ssh. Inilah cara kerjanya:

masukkan deskripsi gambar di sini

Batasannya adalah: penghitung hanya berfungsi untuk tingkat kedalaman 13-14, tergantung pada OS. Saya tidak bermaksud menyelidiki alasannya :)

  • 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
  • cshdan 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
  • shitu sebenarnya dashdi Ubuntu - di sini masalahnya sedikit rumit dan kabel (baca referensi di bawah untuk informasi lebih lanjut):

    1. Edit ~/.profilefile dan tambahkan baris berikut di bagian bawah:

      ENV=$HOME/.shrc; export ENV
    2. Buat file ~/.shrcdengan konten berikut, perhatikan kshjuga membaca $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

Referensi:


Buat perintah yang akan menampilkan kedalaman: Opsi lain adalah membuat perintah shell yang akan menampilkan kedalaman. Untuk tujuan ini, buat file yang dapat dieksekusi (dengan demikian harus dapat diakses oleh seluruh sistem):/usr/local/bin/depth

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

Edit file dengan editor favorit Anda dan tambahkan baris berikut sebagai kontennya:

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

Script di atas memiliki dua opsi -vatau --verboseyang akan menampilkan daftar shell yang terlibat. Dan opsi lain yang akan memeriksa apakah kedalamannya lebih besar dari satu dan berdasarkan ini akan kembali exit 0atau exit 1, sehingga Anda dapat menggunakannya dengan cara ini depth && exit. Berikut adalah beberapa contoh penggunaan:

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

Perbandingan dengan solusi lain: Saya menghabiskan beberapa waktu tambahan untuk mencari tahu beberapa kelemahan dari pendekatan yang diberikan di sini. Saya dapat membayangkan dua kasus berikut (huruf kapital diperlukan untuk penyorotan sintaksis yang lebih baik):

  • Kapan suatau sudo -iterlibat:

    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
  • Ketika ada proses latar belakang diluncurkan:

    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
sumber
Sekarang aku bingung tentang output aku di sistem saya: systemd───xfce4-terminal───bash───pstree. Kenapa begini?
val berkata Reinstate Monica
1
@val: systemd adalah proses init, induk dari semua proses lainnya. Anda tampaknya menggunakan xfce4-terminal, yang meluncurkan bashshell, di mana Anda berlari pstree, yang melaporkan dirinya dan orang tuanya. Jika yang Anda maksud adalah kurangnya langkah-langkah antara systemd dan xfce4-terminal, bisa jadi apa pun yang diluncurkan xfce4-terminal mati atau tidak diakui, dalam hal ini akan diwarisi oleh init.
Nick Matteo
Ada alasan untuk tidak membaca SHLVL? Portabilitas di seluruh proses dan sistem, saya berasumsi, tapi pstree mungkin tidak diinstal ..
D. Ben Knoble
Halo, @ D.BenKnoble, seperti yang dibahas di bawah @ egmont ini jawabannya , $SHLVLtidak didukung oleh beberapa kerang. Lebih spesifik, menurut lingkungan dari demo di atas tidak hanya didukung oleh sh( dash) - dan shell ini tidak dihitung sama sekali oleh variabel ini. Di sisi lain pstreeadalah bagian dari paket psmisc yang menyediakan juga fuser, killalldan beberapa lainnya - ini adalah komponen utama Ubuntu - saya belum menginstalnya pada sistem yang disebutkan dalam jawaban ini.
pa4080
30

Periksa nilai SHLVLvariabel shell:

echo $SHLVL

Mengutip dari bashhalaman buku panduan:

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

Ini juga didukung oleh zsh.

egmont
sumber
4
Tapi sh tidak dihitung, jadi contoh yang diberikan, dengan sh, tidak akan menambah SHLVL. Namun, ini adalah sesuatu yang mungkin berguna bagi mereka yang tidak terlalu banyak mengganti shell
ubfan1
3
@ ubfan1 kecuali ada definisi vimrc utama, :shdefault ke shell login pengguna saya pikir (itu benar-benar bentuk disingkat :shelldaripada nama biner shell tertentu)
steeldriver
3
Saya tidak terbiasa dengan detail vim, tetapi saya sudah mencoba :shdari vimsebelum memposting jawaban ini, dan itu meningkatkan level shell untuk saya. Shell login saya adalah bash.
egmont
9

Di saya .bashrc, saya gunakan $SHLVLuntuk menyesuaikan $PS1, dengan menambahkan +tanda " " ke $SUBSHELLvariabel saya :

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

Lalu, saya bisa melihat seberapa dalam diri saya:

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

Anda dapat menempatkan salah satu dari dua versi dalam file dan menggunakan sumber untuk membuat $ DEPTH tersedia.

# 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
sumber
2

Anda cukup menggunakan pstanpa argumen tambahan untuk melihat seluruh tumpukan shell (termasuk yang sekarang). Ini juga akan menunjukkan semua pekerjaan latar belakang yang telah Anda mulai serta psdirinya sendiri, tetapi itu dapat memberi Anda perkiraan kasar seberapa dalam Anda.

aragaer
sumber
Ini berfungsi { echo hello world; ps; } &untuk membuktikan psjawaban di atas.
WinEunuuchs2Unix
@ WinEunuuchs2Unix, maksud saya kira-kira seperti ini: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080
Apakah ada cara untuk meniru pstree -s $$ dengan ps?
bac0n