The C shell reads its .cshrc, .login, and .logout setup files at particular times (Section 3.3). Only "login" C shells (Section 3.4) will read the .login and .logout files. Back when csh was designed, this restriction worked fine. The shell that started as you logged in was flagged as a login shell, and it read all three files. You started other shells (shell escapes, shell scripts, etc.) from that login shell, and they would read only .cshrc. The same can be said of other shell variants, such as tcsh, though they may have multiple startup files -- the problem of distinguishing between login and nonlogin shell startup is the same.
Now, Unix has interactive shells started by window systems (like xterm (Section 24.20)), remote shells (like rsh (Section 1.21) or ssh), and other shells that might need some things set from the .login or .logout files. Depending on how these shells are invoked, these might not be login shells -- so they might read only .cshrc (or .tcshrc, etc.). How can you handle that? Putting all your setup commands in .cshrc isn't a good idea because all subshells (Section 24.4) read it . . . you definitely don't want to run terminal-setting commands like tset (Section 5.3) during shell escapes!
Most other shells have the same problem. Some, like zsh and bash, have several setup files that are read at different times -- and probably can be set up to do what you want. For other shells, though, you'll probably need to do some tweaking.
To handle problems at login time, put almost all of your setup commands in a file that's read by all instances of your shell, login or nonlogin. (In the C shell, use .cshrc instead of .login.) After the "login-only" commands have been read from the setup file, set the ENV_SET environment variable (Section 35.3) as a flag. (There's nothing special about this name. You can pick any name you want.) You can then use this variable to test whether the login-only commands have already been run and skip running them again in nonlogin shells.
Because the environment variables from a parent process are passed to any child processes it starts, the shell will copy the "flag" variable to subshells, and the .cshrc can test for it. If the variable exists, the login-only commands are skipped. That'll keep the commands from being read again in a child shell.
Here are parts of a .cshrc that show the idea:
...Normal .cshrc stuff... if ($?prompt && ! $?ENV_SET) then # Do commands that used to go in .login file: setenv EDITOR /usr/ucb/vi tset ... setenv ENV_SET done endif
You might put a comment in the file you've bypassed -- the csh .login file, the ksh .profile file, etc. -- to explain what you've done.
The file that runs when you log out (in the C shell, that's .logout) should probably be read only once -- when your last ("top-level") shell exits. If your top-level shell isn't a login shell, you can make it read the logout file anyway. Here's how: first, along with the previous fixes to your .cshrc-type file, add an alias that will read your logout file when you use the exit command. Also set your shell to force you to use the exit command (Section 35.12) to log out -- in csh, for example, use set ignoreeof. Here's what the chunk of your .bashrc will look like:
case Section 35.10, / Section 36.25, function Section 29.11, . Section 35.29
case "$-/${ENV_SET:-no}" in *i*/no) # This is an interactive shell / $ENV_SET was not set earlier. # Make all top-level interactive shells read .bash_logout file: set -o ignoreeof function exit { . ~/.bash_logout builtin exit } ;; esac
The builtin exit (Section 27.9) prevents a loop; it makes sure bash uses its internal exit command instead of the exit function you've just defined. In the C shell, use ""exit (Section 27.10) instead. This isn't needed on all shells though. If you can't tell from your manual page, test with another shell (Section 3.6) and be ready to kill (Section 24.12) a looping shell.
--JP and SJC
Copyright © 2003 O'Reilly & Associates. All rights reserved.