How do I know if I'm running a nested shell?

LinuxBashShellUnix

Linux Problem Overview


When using a *nix shell (usually bash), I often spawn a sub-shell with which I can take care of a small task (usually in another directory), then exit out of to resume the session of the parent shell.

Once in a while, I'll lose track of whether I'm running a nested shell, or in my top-level shell, and I'll accidentally spawn an additional sub-shell or exit out of the top-level shell by mistake.

Is there a simple way to determine whether I'm running in a nested shell? Or am I going about my problem (by spawning sub-shells) in a completely wrong way?

Linux Solutions


Solution 1 - Linux

The $SHLVL variable tracks your shell nesting level:

$ echo $SHLVL
1
$ bash
$ echo $SHLVL
2
$ exit
$ echo $SHLVL
1

As an alternative to spawning sub-shells you could push and pop directories from the stack and stay in the same shell:

[root@localhost /old/dir]# pushd /new/dir
/new/dir /old/dir
[root@localhost /new/dir]# popd
/old/dir
[root@localhost /old/dir]#

Solution 2 - Linux

Here is a simplified version of part of my prompt:

PS1='$(((SHLVL>1))&&echo $SHLVL)\$ '

If I'm not in a nested shell, it doesn't add anything extra, but it shows the depth if I'm in any level of nesting.

Solution 3 - Linux

Look at $0: if it starts with a minus -, you're in the login shell.

Solution 4 - Linux

pstree -s $$ is quite useful to see your depth.

Solution 5 - Linux

The environment variable $SHLVL contains the shell "depth".

echo $SHLVL

The shell depth can also be determined using pstree (version 23 and above):

pstree -s $$ | grep sh- -o | wc -l

I've found the second way to be more robust than the first whose value was reset when using sudo or became unreliable with env -i.

None of them can correctly deal with su.


The information can be made available in your prompt:

PS1='\u@\h/${SHLVL} \w \$ '
PS1='\u@\h/$(pstree -s $$ | grep sh- -o | tail +2 | wc -l) \w \$ '

The | tail +2 is there to remove one line from the grep output. Since we are using a pipeline inside a "$(...)" command substitution, the shell needs to invoke a sub-shell, so pstree report it and grep detects one more sh- level.


In debian-based distributions, pstree is part of the package psmisc. It might not be installed by default on non-desktop distributions.

Solution 6 - Linux

ptree $$ will also show you how many levels deep you are

Solution 7 - Linux

As @John Kugelman says, echo $SHLVL will tell you the bash shell depth.
And as @Dennis Williamson shows, you can edit your prompt via the PS1 variable to get it to print this value.

I prefer that it always prints the shell depth value, so here's what I've done: edit your "~/.bashrc" file:

gedit ~/.bashrc

and add the following line to the end:

export PS1='\$SHLVL'":$SHLVL\n$PS1"

Now you will always see a printout of your current bash level just above your prompt. Ex: here you can see I am at a bash level (depth) of 2, as indicated by the $SHLVL:2:

> $SHLVL:2
> 7510-gabriels ~ $

Now, watch the prompt as I go down into some bash levels via the bash command, then come back up via exit. Here you see my commands and prompt (response), starting at level 2 and going down to 5, then coming back up to level 2:

$SHLVL:2 
7510-gabriels ~ $ bash
$SHLVL:3 
7510-gabriels ~ $ bash
$SHLVL:4 
7510-gabriels ~ $ bash
$SHLVL:5 
7510-gabriels ~ $ exit
exit
$SHLVL:4 
7510-gabriels ~ $ exit
exit
$SHLVL:3 
7510-gabriels ~ $ exit
exit
$SHLVL:2 
7510-gabriels ~ $ 

Bonus: always show in your terminal your current git branch you are on too!

Make your prompt also show you your git branch you are working on by using the following in your "~/.bashrc" file instead:

git_show_branch() {
    __gsb_BRANCH=$(git symbolic-ref -q --short HEAD 2>/dev/null)
    if [ -n "$__gsb_BRANCH" ]; then
        echo "$__gsb_BRANCH"
    fi
}
export PS1="\e[7m\$(git_show_branch)\e[m\n\h \w $ "
export PS1='\$SHLVL'":$SHLVL $PS1"

Source: I have no idea where git_show_branch() originally comes from, but I got it from Jason McMullan on 5 Apr. 2018. I then added the $SHLVL part shown above just last week.

Sample output:

> $SHLVL:2 master
> 7510-gabriels ~/GS/dev/temp $

And here's a screenshot showing it in all its glory. Notice the git branch name, master, highlighted in white!

enter image description here

Update to the Bonus section

I've improved it again and put my ~/.bashrc file on github here. Here's a sample output of the new terminal prompt. Notice how it shows the shell level as 1, and it shows the branch name of the currently-checked-out branch (master in this case) whenever I'm inside a local git repo!:

enter image description here

Cross-referenced:

Solution 8 - Linux

If you running inside sub-shell following code will yield 2:

ps | fgrep bash | wc -l

Otherwise, it will yield 1.

EDIT Ok, it's not so robust approach as was pointed out in comments :)
Another thing to try is

ps -ef | awk '{print $2, " ", $8;}' | fgrep $PPID 

will yield 'bash' if you in sub-shell.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMansoor SiddiquiView Question on Stackoverflow
Solution 1 - LinuxJohn KugelmanView Answer on Stackoverflow
Solution 2 - LinuxDennis WilliamsonView Answer on Stackoverflow
Solution 3 - Linuxmartin claytonView Answer on Stackoverflow
Solution 4 - LinuxmelchiView Answer on Stackoverflow
Solution 5 - LinuxloxaxsView Answer on Stackoverflow
Solution 6 - Linuxglenn jackmanView Answer on Stackoverflow
Solution 7 - LinuxGabriel StaplesView Answer on Stackoverflow
Solution 8 - LinuxVictor SorokinView Answer on Stackoverflow