Mansoor Siddiqui
Mansoor Siddiqui

Reputation: 21643

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

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?

Upvotes: 71

Views: 15844

Answers (8)

Gabriel Staples
Gabriel Staples

Reputation: 52449

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

My latest PS1 prompt

I've improved my PS1 prompt again and put it in my eRCaGuy_dotfiles repo. It is inside my ~/.bash_aliases file, which is sourced by my ~/.bashrc file. Search my ~/.bash_aliases file for both my PS1 entries and the gs_git_show_branch_and_hash function used by them.

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), as well as its short git hash (bac27c7 in this case), whenever I'm inside a local git repo!:

enter image description here

This is really helpful to help me ensure I'm always performing my git operations on the correct branch, and to automatically see whenever I cd into a Git repository.

Cross-referenced:

Upvotes: 3

Dennis Williamson
Dennis Williamson

Reputation: 359845

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.

Upvotes: 24

loxaxs
loxaxs

Reputation: 2279

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.

Upvotes: 4

melchi
melchi

Reputation: 667

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

Upvotes: 14

glenn jackman
glenn jackman

Reputation: 246744

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

Upvotes: 0

Victor Sorokin
Victor Sorokin

Reputation: 11996

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.

Upvotes: -1

John Kugelman
John Kugelman

Reputation: 361547

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

Upvotes: 92

martin clayton
martin clayton

Reputation: 78105

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

Upvotes: 13

Related Questions