サブシェルの深さを知るにはどうすればよいですか?


40

時々、vimからサブシェルを開始するなどのことをし:shます。サブシェルにいるかどうかを知るにはどうすればexit1レベルだけ返されるのか、exitログアウトするかセッションを終了する最も外側のシェルにいるのかを知るにはどうすればよいですか。

回転できるインセプショントーテムや、自分のレベルがいくつあるかを知るための何かがありますか?



1
こんにちは!サブシェルを使用しているかどうかを確認する簡単な方法の1つですecho $0。トップレベルシェルの場合、ダッシュで始まる可能性があります。(これは少なくともbashには当てはまります。ダッシュは、いわゆるログインシェルであることを意味します。)
jpaugh

回答:


40

コマンドを使用できますpstree(Ubuntuにはデフォルトで付属しています)。次に例を示します。現在、WSLで開いているターミナルウィンドウは1つだけです。

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のアイデアに基づいて、レベルが1よりも深いときに複数の異なるシェルのプロンプトの前にカウンターを置くために、デモの下に示す行を追加しました。関連する実行コマンド~/.*rc)ファイルの下部。

gnome-terminal、tty、およびsshセッション内で、WSL、Ubuntu 16.04、Ubuntu 18.04(サーバー/デスクトップ)、Ubuntu 19.04でテストを行いました。これがどのように機能するかを次に示します。

ここに画像の説明を入力してください

制限は次のとおりです。OSに応じて、カウンターは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

上記のスクリプトには2つのオプションがあります。-vまたは--verbose、関連するシェルのリストを出力します。そして、深さが1よりも大きいかどうかを確認し、これに基づいて戻るexit 0exit 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

他のソリューションによる比較:ここで提供されているアプローチの弱点を見つけるために、さらに時間を費やしました。次の2つのケースを想像できました(構文の強調表示を改善するには大文字が必要です)。

  • 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はinitプロセスであり、他のすべてのプロセスの親です。どうやらxfce4-terminal、を使用してbashシェルを起動し、その中で自分pstree自身とその親を報告しました。systemdとxfce4-terminalの間にステップがないことを意味する場合、xfce4-terminalを起動したものは何でもそれが死んだか否認された可能性があり、その場合はinitによって継承されます。
ニックマッテオ

読まない理由はSHLVL?プロセスとシステム間の移植、私が想定したが、その後pstreeは、インストールされない場合があり...
D.ベンKnoble

こんにちは、@ D.BenKnobleは@egmontの答えで説明されているように、$SHLVL一部のシェルではサポートされていません。より具体的には、上記のデモの環境によると、shdash)だけではサポートされていません-このシェルはこの変数ではまったくカウントされません。一方pstree、パッケージの一部であるpsmiscのも提供しfuserkillallそしていくつかの他-私はこの答えに記載されたシステム上でそれをインストールしていない-それは、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です。
エグモント

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

2つのバージョンのいずれかをファイルに配置し、ソースを使用して$ 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.