Звідки я можу знати, скільки глибоких ядерних снарядів?


40

Іноді я роблю такі речі, як запуск під-оболонки з vim з :sh. Як дізнатись, чи перебуваю я в підрозділі, де exitпросто поверне мене на один рівень, порівняно з тим, що я перебуваю у самій зовнішній оболонці, де exitвийдуть із мене або закриють сеанс.

Чи є якийсь тотем Першовідкриття, який я можу крутити, або щось, щоб знати, на скільки я глибоких рівнів?



1
Привіт! Один з швидких способів зрозуміти, перебуваєте ви в передплаті чи ні echo $0. Якщо це оболонка вершини, вона, ймовірно, почнеться з тире. (Це справедливо принаймні для bash, а тире означає, що це так звана оболонка для входу.)
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 , для того щоб мати лічильник в передній частині запиту для декількох різних оболонок, коли рівень глибший за один, я додав рядки, показані нижче демонстрації, внизу відповідних файлів команд ( ~/.*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-терміналом, то, можливо, що б запущений xfce4-термінал не загинув або відмовився від нього, у цьому випадку він буде успадкований init.
Нік Маттео

Будь-яка причина не читати SHLVL? Переносність між процесами та системами, я припускаю, але тоді pstree не може бути встановлена ​​..
D. Ben Knoble,

Привіт, @ D.BenKnoble, як це обговорюється в рамках @ Егмонт відповіді , $SHLVLне підтримуються деякими оболонками. Більш конкретно, відповідно до середовища з демонстраційного версії він не підтримується тільки sh( dash) - і ця оболонка зовсім не враховується цією змінною. З іншого боку, pstreeце частина пакету psmisc, який також надає fuser, killallі мало хто інший - це головний компонент Ubuntu - я не встановив його в системах, згаданих у цій відповіді.
pa4080

30

Перевірте значення SHLVLзмінної оболонки:

echo $SHLVL

Цитуючи з bashсторінки керівництва:

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

Його також підтримує zsh.


4
Але sh не зараховується, тому приведений приклад з sh не збільшував би SHLVL. Але це може бути корисно для тих, хто не перемикає снаряди занадто сильно
ubfan1,

3
@ ubfan1, якщо не існує переважаючого визначення vimrc, :shза замовчуванням я вважаю оболонку входу користувача, я думаю (це дійсно скорочена форма, :shellа не назва конкретної бінарної оболонки)
steeldriver

3
Я не знайомий з деталями VIM, але я випробував :shвід vimперш ніж відправляти цю відповідь, і він зробив приріст рівня оболонки для мене. Моя оболонка для входу є 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без додаткових аргументів, щоб побачити весь стек оболонки (включаючи поточний). Він також показуватиме всі фонові завдання, які ви розпочали так само, як і psсам, але це може дати вам приблизну оцінку того, наскільки ви глибокі.


Це { echo hello world; ps; } &допомагає довести psвідповідь вище.
WinEunuuchs2Unix

@ WinEunuuchs2Unix, я маю на увазі щось подібне: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080

Чи є спосіб імітувати pstree -s $$ за допомогою ps?
bac0n
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.