## 4.12. Show Subshell Level with $SHLVL

If you're like me, when you start a shell escape (Section 17.21) or any subshell (Section 24.4), you can forget that you aren't in your login shell. Your shell history (Section 30.1) might get confused, shell variables (Section 35.9) may not be set, and other problems may come up. zsh and bash have a built-in SHLVL environment variable (Section 35.3) that lets you track how many subshells deep your current shell is. tcsh has a shlvl shell variable that's automatically set from (and sets) SHLVL. So, all three shells cooperate with each other to set the right value, even if you start one shell from another. (For other shells that don't have SHLVL -- ksh and csh -- you can set up something similar with a bit of arithmetic in the ENV (Section 35.5) file or the .cshrc file, respectively.)

In your top-level shell, the value of `$shlvl` is 1 (one). In the first subshell, it's 2; in a sub-subshell, it's 3; and so on. You can use this to control your shell startup files -- for example, have some commands in your .cshrc that run when you first log in (and `$shlvl` is 1), but don't run in subshells. You can also put `$shlvl` in your prompt (but only during subshells, if you'd like -- as a reminder that you aren't in your top-level shell). You can set your prompt to `mike%` in top-level shells, `(1)` `mike%` in a first-level subshell, `(2)` `mike%` in a second-level subshell, and so on. Here's some sample prompt-setting code for your .tcshrc:

>     # If this is a subshell, put shell level in prompt:
>     if ($shlvl == 1) then
>         set prompt="${USER}% "
>     else
>         set prompt="($SHLVL) ${USER}% "
>     endif

bash doesn't need an if because login shells read your .bash_profile (or .profile) and subshells read your .bashrc. Here are commands to set the prompts I mentioned earlier:

>     PS1='\u\$ '             ...for the .bash_profile
>     PS1='($SHLVL) \u\$ '    ...for the .bashrc

Does your account run a windowing system that's started from your top-level shell startup file (like .login)? If it does, lines like the following examples (these are for .login) will reset SHLVL so that the shell in the window will start at a SHLVL of 1 -- and act like a top-level shell. This code assumes that your first login shell starts on a tty named /dev/tty1 through /dev/tty6 (which are the Linux virtual consoles (Section 23.12)) and that the windows that open won't have a tty with the same name (which is true on Linux). (If you aren't sure, check who (Section 2.8).) You may need to adapt this. The trick is to make SHLVL 0 (zero) before you start the windowing system. When the windows' shells start, they'll raise SHLVL to 1:

>     # If on a virtual console, bury this shell and start X right away:
>     if ("`tty`" =~ /dev/tty[1-6]) then
>        setenv SHLVL 0
>        startx
>     endif

Getting this to work right in every situation (rsh (Section 1.21), ssh, su, shell escapes (Section 17.21) -- both interactive and noninteractive (Section 3.4) -- subshells, window systems, at jobs (Section 25.5), and so on) can be a challenge (Section 3.8)! It takes a little planning. Sit down and think about all the ways you start subshells -- which subshells are interactive and which aren't -- and whether they'll get SHLVL passed from their parent process. (If you aren't sure, test that with an env or printenv command (Section 35.3).) Then plan which kind of shell needs which SHLVL settings. If it gets too complicated, make it work in most cases! If you use many subshells, this system can be too handy to ignore.

--JP and SJC

Copyright © 2003 O'Reilly & Associates. All rights reserved.