Как я могу узнать, сколько у меня подуровней?

40

Иногда я делаю такие вещи, как запуск суб-оболочки из vim с помощью :sh. Как я узнаю, что если я нахожусь в под-оболочке, где exitпросто верну меня на один уровень, вместо того, чтобы находиться во внешней оболочке, где exitя выйду из системы или закрою сеанс.

Есть ли какой-нибудь тотем Начала, который я могу вращать, или что-то, чтобы узнать, на каком уровне я нахожусь?

Уик
источник
5
Связано с vi.stackexchange.com: Как узнать, что я нахожусь в оболочке от команды vi: sh?
стальная отвертка
1
Здравствуй! Один из быстрых способов узнать, являетесь ли вы недоработкой или нет - это echo $0. Если это оболочка верхнего уровня, она, вероятно, начнется с тире. (Это верно, по крайней мере, для bash, а dash означает, что это так называемая оболочка входа в систему.)
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
pa4080
источник
Теперь я озадачен выход я получил на моей системе: systemd───xfce4-terminal───bash───pstree. Почему так?
говорит Вэл, восстанови Монику
1
@val: systemd является процессом init, родителем всех других процессов. Вы, видимо, используете xfce4-terminal, который запустил bashоболочку, внутри которой вы запустили pstree, которая сообщила о себе и своих родителях. Если вы имеете в виду отсутствие шагов между systemd и xfce4-терминал, то может случиться так, что любой запущенный xfce4-терминал умрет или откажется от него, и в этом случае он будет унаследован init.
Ник Маттео
Любая причина не читать SHLVL? Переносимость между процессами и системами, я полагаю, но тогда pstree может быть не установлена ​​..
Д.Бен Нобл
Здравствуйте, @ 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а не имя конкретного двоичного
файла
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)$ 
waltinator
источник
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}"
bac0n
источник
2

Вы можете просто использовать psбез каких-либо дополнительных аргументов, чтобы увидеть весь стек оболочки (включая текущий). Он также будет показывать все фоновые задания, которые вы запустили, а также psсам, но он может дать вам приблизительную оценку того, насколько вы глубоки.

aragaer
источник
Это работает, { echo hello world; ps; } &чтобы доказать psответ выше.
WinEunuuchs2Unix
@ WinEunuuchs2Unix, я имею в виду что-то вроде этого: paste.ubuntu.com/p/6Kfg8TqR9V
pa4080
Есть ли способ имитировать pstree -s $$ с помощью ps?
bac0n