[Chapter 7] 7.9 Show Subshell Level with $SHLVL
Author: Jerry Peek, Tim O'Reilly & Mike Loukides
Date: 1998-08-04T21:33:52Z

#

| Chapter 7 Setting Your Shell Prompt |

## 7.9 Show Subshell Level with $SHLVL

If you're like me, when you start a shell escape (30.26) or any subshell (38.4), you can forget that you aren't in your login shell. Your shell history (11.1) might get confused, shell variables (6.8) may not be set, and other problems may come up. tcsh (8.3) and bash (8.2) have a built-in SHLVL environment variable (6.1) that lets you track how many subshells deep your current shell is. This article shows how to set up SHLVL for the C shell. (You could use a similar setup with ksh, if you set its ENV (6.3) environment variable.) tcsh (and the csh setup below, too) also have a shlvl shell variable with the same information.

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 (7.1) (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 .cshrc:

>     # 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 above:

>     PS1='\u\$ '             ...for the .bash_profile

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

Both bash and tcsh use the same environment variable, so you can start one shell from the other and the level will be correct. Here's how to make csh work with tcsh and bash-or work on its own, if you don't use the other shells. Put the following lines in your .cshrc file: [1]

> [1] Do you use both csh and tcsh-and use the same .cshrc file for both shells? tcsh shouldn't execute the three lines starting with `set shlvl`; it already sets those variables. Surround those three lines with `if (! $?tcsh)` and `endif`.

    shlvm

    $?prompt
    # Stuff for top-level logins and rsh's...
    if (! $?SHLVL) then
        # This section read by both interactive and non-interactive shells.
        #
        # $SHLVL has never been set.  Set it to show that this is
        # is a top-level shell; increment it to 1 below:

        setenv SHLVL 0
        ...
    endif

    # Set shell level (depth of nested shells).
    # (Note: csh can't do arithmetic on environment variables.)
    set shlvl = $SHLVL
    @ shlvl++
    setenv SHLVL $shlvl

    if ($?prompt) then
        # This section only read by interactive shells:
        ...put prompt-setting code (from above) here
    endif

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 examples below (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 the tty named /dev/console, and that the windows that open won't have a tty named /dev/console. (If you aren't sure, check who (51.4).) 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 workstation console, bury this shell and run X right away:
>     if ("`/bin/tty`" == /dev/console) then
>        setenv SHLVL 0
>        xinit            # Start X window system
>     endif

Getting this to work right in every situation (rsh (1.33), su (22.22), shell escapes (30.26)- both interactive and noninteractive, subshells, window systems, at jobs (40.3), and so on) can be a challenge (2.7)! 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 (6.1)). 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 is too handy to ignore.

- JP

7.8 Highlighting in Shell Prompts |  | 7.10 What Good Is a Blank Shell Prompt?