Memra

Start-up files and the exported environment

◈ 10 cards

Say which C shell start-up file runs when and why that decides where an alias belongs, then extract the environment variables a user's dot-files export and report each one's live value.

Two files, two lifetimes

The C shell reads two personal start-up files, and the whole of their design follows from when each one runs.

~/.cshrc is read by every C shell, login or not. Open a terminal, run a C shell script, start a sub-shell with csh — each of those reads .cshrc afresh.

~/.login is read once, by a login shell only, immediately after .cshrc. Log in, and it runs. Start ten sub-shells afterwards and it does not run again.

Watch the difference directly:

% grep echo ~/.cshrc ~/.login
/home/ada/.cshrc:echo "cshrc ran"
/home/ada/.login:echo "login ran"
% csh
cshrc ran
% exit
%

The sub-shell announced .cshrc and said nothing about .login. That single observation decides where everything goes.

Anything that is not inherited must go in .cshrc. Aliases and shell-local variables set with set are properties of one shell process; a child does not get them. If you put an alias in .login it exists in your login shell and vanishes in every sub-shell, which is exactly the bug report "my alias works in this window but not in scripts". Aliases, set variables, prompt, history size: all .cshrc.

Anything that is inherited, or is a property of the session rather than the shell, goes in .login. Exported variables created with setenv are inherited by every child, so setting them once at login is enough — repeating them in .cshrc just does the same work again in every sub-shell. Your umask is inherited too. So are the one-off session jobs: setting the terminal type, printing the message of the day, checking for mail.

What setenv leaves behind, and what set does not

Only setenv exports. This is the fact the assignment turns on. set path = (...) and setenv PATH ... look like the same operation on the same thing, and they are not: path is a shell-local word list, PATH is the exported colon-separated string, and the C shell keeps them synchronised as a special case for that one pair (and for term/TERM, user/USER, cwd/PWD). No such link exists for a variable you invent. set EDITOR = vi in your .cshrc gives you an EDITOR that no editor, pager or mail program will ever see.

The two namespaces are genuinely separate, which produces one memorably confusing session:

% setenv NAME ada
% set NAME = lovelace
% echo $NAME
lovelace
% unset NAME
% echo $NAME
ada

unset removed the shell-local one and revealed the environment one still standing behind it. It looks like the unset failed. Clearing an exported variable takes unsetenv.

Worked example: reporting what the dot-files export

The task is to read both start-up files, list every variable they export, and report each one's value. Three decisions make it correct.

Select setenv lines only. A set line is not an export, so a script that greps for both over-reports. Anchor the pattern so an occurrence inside a comment or a longer word does not match: grep '^[[:space:]]*setenv'.

Take the name from field 2. The setenv form is setenv NAME value, so the name is the second whitespace-separated field and everything after it is the value.

Report the live value, not the text in the file. This is the part that separates a working script from a plausible one. A variable may be exported in .login and exported again in .cshrc, or overridden later in the same file, or overridden by the session after both files ran. The literal text on the line is only the last word if nothing touched it since. Asking the running environment with printenv "$name" always gives the value that is actually in force, and it makes the "set in both files" case correct for free.

#!/bin/csh
foreach file ($HOME/.login $HOME/.cshrc)
    if (! -r $file) continue
    echo "--- $file ---"
    foreach name (`grep '^[[:space:]]*setenv' $file | awk '{print $2}'`)
        echo "$name = `printenv $name`"
    end
end

The standard variables you will find, and what each is for, are in the figure below. Explaining them is half the marks on this question.

csh local / exportedWhat it holdsWho reads ithome / HOMEabsolute path of your homedirectorycd, ~ expansion, almostevery programpath / PATHword list vs colon stringof command directoriesthe shell, on every commandlookupshell / SHELLpathname of your loginshellterminals, vi and less whenthey shell outterm / TERMterminal type name, e.g.xterm-256colorcurses, vi, less, clearcwd / PWDthe current workingdirectorythe prompt, and tools thatprint pathsuser / USERyour login namemail, ps, write, mostbanner text(none) / EDITORpreferred editor commandcrontab, git, mail - neverthe shell itself(none) / MAILpath of your mailbox filethe mail reader and theshell mail checkprompt / (none)the primary prompt stringthe shell ONLY - neverexportedEDITOR has no local twin: set EDITOR = vi exports nothing and no editor will see it.
Only the first six pairs are linked by the shell itself. For any variable you invent, `set` and `setenv` are unrelated.
NORMAL ~/memra/learn/comp-325/csh-startup-files-and-the-exported-environment utf-8 LF