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 withif (! $?tcsh)
andendif
.
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.
-