| CONTENTS |
When you first log in to a Unix system, the login program performs various security measures. These vary slightly from Unix variant to Unix variant, but they are largely the same.
First, login checks to see if you are not the root user and whether the file /etc/nologin exists (it is created by the shutdown command to indicate that the system is being brought down for maintenance). If both of these conditions are true, the login program prints the contents of that file to the terminal, and the login fails. If you are the root user, however, you will be allowed to log in.
Second, login checks to see if there are any special conditions on your login attempt (which may be defined in /etc/usertty or /etc/securetty), such as on which tty you're coming in. Linux systems do this, for example. Some systems (such as Darwin and other BSD-based systems) also check /etc/fbtab and may restrict your access to any devices listed in that file. These systems may also log failed login attempts to a file, such as /var/log/failedlogin, if it exists.
login may also record the time of login in the file /var/log/lastlog, make an entry in the file /var/run/utmp, showing that you are successfully logged in (it is removed once you log out), and append the utmp entry to the file /var/log/wtmp , showing that you logged in. This wtmp record will be updated on logout, showing the duration of your login session.
If the file .hushlogin exists in the user's home directory, the login will be quiet; otherwise, the following sequence of events will occur. If the system has any special copyright information to display, it will be printed to the terminal, followed by the message of the day (usually stored in /etc/motd ), and the user's last login time and system of origin (from the wtmp file, as discussed in the previous paragraph). If you want your login to be quiet, simply touch ~/.hushlogin. If you want it to be noisy, remove the file.
Finally, if all other checks have passed and restrictions have been performed, login starts a shell for you. Which shell depends on what is set in your user database entry (/etc/passwd, NIS, or possibly NetInfo under Darwin). If the shell specified for you is not interactive (Section 3.4), you may well be denied a command line. This is common for POP and ftp-only user accounts, where /bin/true and /bin/false are often specified as shells to disallow shell logins from those accounts.
JP and SJC
Throughout the book, we will refer to terminals, terminal emulators, and other software that allows you, the end user, to interact with the computer via some character-driven screen. In the old days, most terminals were separate hardware, but nowadays they're usually software. Mac OS X is no exception: its Terminal application, found in the Utilities folder of your Applications folder, is a terminal emulator.
You can launch Terminal by double-clicking on the icon in the Finder, or if you have the Terminal icon in your Dock, by single-clicking on that icon.
Once launched, Terminal may be configured as most Mac applications can: by setting preferences in the Preferences dialog and choosing a font family and size from the Font menu.
One big difference between Terminal and other, X-specific applications is that instead of running individual instances of xterm, you run one instance of Terminal and may have multiple windows, known as "shells," which may have saved settings (such as color, size, font choice, and various other settings). You can't run a shell in Mac OS X without running Terminal.
SJC
To understand setup files, you need to understand that a shell can act like a login shell or a nonlogin shell (Section 3.4). There are different setup files for nonlogin and login shells.
When you log in to a Unix system but not under a window system the login program starts a shell for you. The login program sets a special flag (Section 3.19) to tell a shell that it's a login shell. If the shell doesn't have that flag set, it won't act like a login shell. Opening a new window in a window system may or may not set the "login shell" flag that depends on the configuration. (For example, the command xterm -ls starts a login shell in an xterm window (Section 24.20); xterm +ls starts a nonlogin shell.) When you connect to a system with programs like ftp and scp, that usually starts a nonlogin shell. And a subshell (Section 24.4) is never a login shell (unless you set a command-line option to force a login shell, like bash -l).
How can you tell whether your shell is a login shell? The answer is "it depends." When you first log in to a system, you want a login shell that sets things like the terminal type (Section 5.2, Section 5.3). Other shells on the same terminal should be nonlogin shells to avoid redoing those one-time-only setup commands. Different shells have their own methods for handling first-time shell invocations versus later invocations, and that's what the rest of this article is about.
Parenthesis operators (Section 43.7) don't read any setup file. Instead, they start another instance of your current shell. Parentheses are called "subshell operators," but the subshell they start doesn't print a prompt and usually has a short lifetime.
Next, let's look at the setup files login and nonlogin for the major shells. I recommend that you read about all of them. Then experiment with your shell's setup files until you get things working the way you want them.
Your login(1) command probably sets some environment variables ( Section 35.3) like HOME, PATH, SHELL, TERM, MAIL, and LOGNAME or USER; check its manual page. Your system may set some environment variables or other parameters that apply to all shells or to all shells of a particular type (all bash shells, zsh shells, etc.). All of these will be passed through the environment, from parent process to child process (Section 35.4), to all of your shells, login and nonlogin.
Once login or your window system starts your individual shell, it may also read its own system-wide setup files. These files, if any, will be read before your personal setup files. Check your shell's manual page and the /etc directory for files like csh.login, bashrc, zshrc, and so on. On Red Hat systems, for example, there is a directory named /etc/profile.d containing package-specific C and Bash shell config files that are sourced (read into the current shell) on startup of a shell. On Mac OS X, when you use Terminal (Section 3.2), your shell (which is tcsh by default) reads /private/etc/csh.cshrc, as well as any user-specific files (e.g., ~/.tcshrc).
The original Bourne shell has one file that it reads when you log in: it's called .profile and is in your home directory. Put all your setup commands there. Later versions of the Bourne shell may also read /etc/profile before your local setup file is read and may also read the contents of whatever file is named in the ENV environment variable (Section 35.3) (but only for interactive shells). You may set this variable from your own .profile:
ENV=$HOME/.mystartup; export ENV
The Bourne shell doesn't read .profile when you start a nonlogin shell or subshell (Section 43.7), though. Subshells are set up through inheritance of environment variables (Section 35.3) that were set when you first logged in (in system-wide setup files or .profile) or from commands you typed since.
C shell users have several shell setup files available:
The .cshrc file is read any time a C shell starts that includes shell escapes and shell scripts.[1] This is the place to put commands that should run every time you start a shell. For instance, shell variables like cdpath (Section 31.5) and prompt should be set here. Aliases (Section 29.2) should, too. Those things aren't passed to subshells through the environment, so they belong in .cshrc (or .tcshrc). See the upcoming section on tcsh for more details.
Alternately, you can put aliases into a separate file and use the source command to read the file into the current shell from your .cshrc/.tcshrc if you're the sort who likes to have custom init files for every host you log in to, but like your aliases to be common wherever you go. This provides a quick and easy way for you to copy your .csh.aliases (or whatever name you give it, being careful to distinguish between it and the slightly different format required by bash aliases) from host to host without clobbering your custom, localized init files.
When csh starts up, on recent systems it may read a system-wide setup file, such as /etc/csh.cshrc,[2] and for login shells, /etc/csh.login.
Your .login file is read when you start a login shell. You should set several things here. Set environment variables (Section 35.3) (which Unix will pass to subshells automatically). Run commands like tset (Section 5.3) and stty (Section 5.7, Section 5.8) that set up your terminal. Finally, include commands you want to run every time you log in checking for mail and news (Section 1.21), running fortune, checking your calendar for the day, etc.
Note that .cshrc is read before .login, by csh, but that tcsh may be compiled such that the order is reversed, and .tcshrc may be read after .login in some environments. Check the version shell variable to find out how your environment is set up.
The shell reads .logout when you end a login shell. Section 3.8 has tips for reading .logout from nonlogin shells.
The Korn shell is a lot like the Bourne shell. A login Korn shell (Section 3.4) will read the .profile first; recent versions do so only after reading /etc/profile, if present. The .profile can set the ENV (Section 35.5) environment variable to the pathname of a file (typically $HOME/.kshrc). Any child Korn shell started by that login shell including all subshells will read the file named by $ENV as it starts up, before printing a prompt or running other commands.
The public domain Korn shell often found on Linux may also be further restricted when invoked as a "privileged" shell, using a pattern that matches r*sh, in which case neither the ~/.profile nor the file named by the ENV environment variable will be read. Instead, the shell will be initialized using /etc/suid_profile, if present.
bash is something of a cross between the Bourne and C shells. A login bash will read .bash_profile , .bash_login, or .profile. A noninteractive bash will read a file named .bashrc in your home directory. The shell reads .bash_logout when you end a login shell; you can set a trap (Section 4.18) to handle nonlogin shells.
bash also uses GNU Readline for reading and editing text you enter at a shell prompt. The .inputrc file configures Readline for a given user; /etc/inputrc is for global configuration.
tcsh is like the C shell but more flexible. If a tcsh shell is run, it first tries to read .tcshrc and, if not found, then tries .cshrc. In addition, tcsh will also load either .history or the value of the histfile variable, if set; then it may try to read .cshdirs or the value of the dirsfile variable.
As always, zsh is very flexible. Startup files are read from the directory named in the ZDOTDIR environment variable, if any;[3] otherwise, from HOME. All shells read the global /etc/zshenv and your .zshenv files. If the shell is a login shell, commands are read from /etc/zprofile and then your .zprofile. Then, if the shell is interactive, commands are read from /etc/zshrc and your .zshrc. Finally, if the shell is a login shell, /etc/zlogin and your .zlogin files are read.
JP and SJC
Each Unix shell (sh, csh, etc.) can be in interactive mode or noninteractive mode. A shell also can act as a login shell or a nonlogin shell. A shell is a shell is a shell e.g., a login bash shell is the same program (like /bin/bash) as a nonlogin bash shell. The difference is in the way that the shell acts: which setup files it reads, whether it sets a shell prompt, and so on.
When you first log in to a Unix system from a terminal, the system normally starts a login shell. (Section 3.4) A login shell is typcally the top-level shell in the "tree" of processes that starts with the init (Section 24.2) process. Many characteristics of processes are passed from parent to child process down this "tree" especially environment variables (Section 35.3), such as the search path (Section 35.6). The changes you make in a login shell will affect all the other processes that the top-level shell starts including any subshells (Section 24.4).
So, a login shell is where you do general setup that's done only the first time you log in initialize your terminal, set environment variables, and so on. A shell "knows" (Section 3.19) when it's a login shell and, if it is, the shell reads special setup files (Section 3.3) for login shells. For instance, login C shells read your .login file, and Bourne-type login shells read .profile. Bash may also read /etc/profile, and ~/.bash_profile or ~/.bash_login or ~/.profile, depending on whether those files exist and whether the -noprofile option has been passed (which would disable reading of any startup files).
Nonlogin shells are either subshells (started from the login shell), shells started by your window system (Section 24.20), or "disconnected" shells started by at (Section 25.5), rsh (Section 1.21), etc. These shells don't read .login or .profile. In addition, bash allows a nonlogin shell to read ~/.bashrc or not, depending on whether the -norc or -rcfile options have been passed as arguments during invocation. The former simply disables reading of the file, and the latter allows a substitute file to be specified as an argument.
Some shells make it easy to know if a particular invocation is a login shell. For instance, tcsh sets the variable loginsh. Check your shell's manual page for details. Section 4.12 shows another solution: the SHLVL variable that's set in most modern shells. Or you can add the following line to the beginning of a setup file that's only read by login shells (Section 3.3). The line sets a shell variable (Section 35.9) named loginshell:
set loginsh=yes ...csh loginshell=yes ...bash and other sh-type shells
Now wherever you need to know the type of shell, use tests like:
if ($?loginsh) ...csh-type shells if [ -n "$loginshell" ] ...sh-type shells (including bash)
This works because the flag variable will only be defined if a shell has read a setup file for login shells. Note that none of the variable declarations use the "export" keyword this is so that the variable is not passed on to subsequent shells, thereby ruining its purpose as a flag specific to login shells.
In general, shells are used for two jobs. Sometimes, a shell handles commands that you type at a prompt. These are interactive shells. Other times, a shell reads commands from a file a shell script (Section 35.2). In this case, the shell doesn't need to print a prompt, to handle command-line editing, and so on. These shells can be noninteractive shells. (There's no rule that only noninteractive shells can read shell scripts or that only interactive shells can read commands from a terminal. But this is generally true.)
One other difference between interactive and noninteractive shells is that interactive shells tie STDOUT and STDERR to the current terminal, unless otherwise specified.
It's usually easy to see whether a particular invocation of your shell is interactive. In C shells, the prompt variable will be set. In the Korn shell and bash, the -i flag is set. Your current flags may be displayed using the $- variable:
prompt$ echo $- imH
The previous example, from an interactive bash shell, shows that the flags for an interactive shell (i), monitor mode (m), and history substitution (H) have been set.
JP and SJC
Setup files for login shells ( Section 3.4) such as .login and .profile typically do at least the following:
Set the search path (Section 27.6) if the system default path isn't what you want.
Set the terminal type (Section 5.3) and make various terminal settings (Section 5.7, Section 5.8) if the system might not know your terminal (if you log in from various terminals over a dialup line or from a terminal emulator on a desktop machine, for instance).
Set environment variables (Section 35.3) that might be needed by programs or scripts that you typically run.
Run one or more commands that you want to run whenever you log in. For example, if your system login program doesn't show the message of the day, your setup file can. Many people also like to print an amusing or instructive fortune. You also might want to run who (Section 2.8) or uptime (Section 26.4) or w (a combination of the other two, but not found on all systems) for information about the system.
In the C shell, the .cshrc file is used to establish settings that will apply to every instance of the C shell, not just login shells. For example, you typically want aliases (Section 28.2) available in every interactive shell you run but these aren't passed through the environment, so a setup file has to do the job. You may wish to put all of your aliases into another file, such as .aliases, or qualify the name with the shell's name, such as .csh.aliases, to allow for different alias formats between shells, and then you can use the source command to read in that file on startup from .cshrc.
Even novices can write simple setup files. The trick is to make these setup scripts really work for you. Here are some of the things you might want to try:
Creating a custom prompt.
Coordinating custom setup files on different machines (Section 3.18).
Making different terminal settings depending on which terminal you're using (Section 3.10 and others).
Seeing the message of the day only when it changes.
Doing all of the above without making your login take forever.
TOR and SJC
The shell is your interface to Unix. If you make a bad mistake when you change your setup file (Section 3.3) or your password, it can be tough to log in and fix things.
Before you change your setup, it's a good idea to start a login session to the same account from somewhere else. Use that session for making your changes. Log in again elsewhere to test your changes.
Don't have a terminal with multiple windows or another terminal close to your desk? You can get the same result by using rlogin or telnet (Section 1.21) to log in to your host again from the same terminal. What I mean is:
somehost% vi .cshrc
...Make edits to the file...
somehost% rlogin localhost
...Logs you in to your same account...
An error message
somehost% logout
Connection closed.
somehost% vi .cshrc
...Edit to fix mistake...
If you don't have rlogin or telnet, the command su - username, where username is your username, will do almost the same thing. Or, if you're testing your login shell configuration, login will do as well.
JP and SJC
One common mistake in shell setup files (Section 3.3) is lines like these:
$$ Section 27.17, `...` Section 28.14
source .aliases echo "Shell PID $$ started at `date`" >> login.log
What's wrong with those lines? Both use relative pathnames (Section 1.16) for the files (.aliases, login.log), assuming the files are in the home directory. Those lines won't work when you start a subshell (Section 24.4) from somewhere besides your home directory because your setup files for nonlogin shells (like .cshrc) are read whenever a shell starts. If you ever use the source or . commands (Section 35.29) to read the setup files from outside your home directory, you'll have the same problem.
Use absolute pathnames instead. As Section 31.11 explains, the pathname of your home directory is in the tilde (~) operator or the $HOME or $LOGDIR environment variable:
source ~/.aliases echo "Shell PID $$ started at `date`" >> ~/login.log
JP
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
Lots of users add an if (! $?prompt) exit test to their .cshrc files. It's gotten so common that some vendors add a workaround to defeat the test. For instance, some versions of the which command (Section 2.6) set the prompt variable so that it can see your aliases "hidden" inside the $?prompt test. I've also seen a version of at that starts an interactive shell to run jobs.
If you've buried commands after if (! $?prompt) that should only be run on interactive shells or at login time, then you may have trouble.
There are workarounds. What you'll need depends on the problem you're trying to work around.
The version of which on the CD-ROM [see http://examples.oreilly.com/upt3] works without reading your .cshrc file, so there's no problem there.
Here's a way to stop the standard which from reading parts of your .cshrc that you don't want it to read. The first time you log in, this scheme sets a CSHRC_READ environment variable (Section 35.3). The variable will be copied into all subshells (Section 24.4) (like the one that which starts). In subshells, the test if ($?CSHRC_READ) will branch to the end of your .cshrc file:
if (! $?prompt) goto cshrc_end # COMMANDS BELOW HERE ARE READ ONLY BY INTERACTIVE SHELLS: alias foo bar ... if ($?CSHRC_READ) goto cshrc_end # COMMANDS BELOW HERE ARE READ ONLY AT LOGIN TIME: setenv CSHRC_READ yes ... cshrc_end:
If you have a buggy version of at (Section 25.5) that runs jobs from interactive shells, make your own frontend to at (Section 29.1) that sets an environment variable named AT temporarily before it submits the at job. Add a test to your .cshrc that quits if AT is set:
( ) Section 43.7, \at Section 29.8
# at JOBS RUN INTERACTIVE SHELLS ON MY BUGGY VERSION OF UNIX. # WORKAROUND IS HERE AND IN THE at ALIAS BELOW: if ($?AT) goto cshrc_end ... alias at '(setenv AT yes; \at \!*)' ... cshrc_end:
Most modern versions of at save a copy of your environment when you submit the job and use it when the at job is run. At that time, the AT environment variable will be set; the C shell will skip the parts of your .cshrc that you want it to. It's ugly, but it works.
Those workarounds probably won't solve all the problems on your version of Unix, but I hope they'll give you some ideas.
JP and SJC
If you work at several kinds of terminals or terminal emulators, terminal setup can be tough. For instance, my X terminal sends a backspace character when I push the upper-right key, but the same key on another terminal sends a delete character I want stty erase (Section 5.8) to set the correct erase character automatically.[4] Maybe you want a full set of calendar programs started when you log in to the terminal at your desk, but not when you make a quick login from somewhere else.
The next seven articles have ideas for changing your login sequence automatically. Some examples are for the C shell and use that shell's switch and if. Examples for Bourne-type shells use case (Section 35.10) and if (Section 35.13). If you use the other type of shell, the idea still applies; just swap the syntax.
If you use several kinds of terminals or terminal emulators, try testing the TERM environment variable (Section 3.11). Testing other environment variables (Section 3.14) can identify the frontend system (like a window system) you're using.
Test the output of who am i (Section 3.12) to find out about the remote system from which you've logged in.
If you log into different kinds of ports network, hardwired, and so on search for the port type (Section 3.15) in a table like /etc/ttys (in BSD derivatives) or /etc/inittab (in some other variants). Testing the port name (Section 3.13) may also work.
In the X Window System, you can test the window size (Section 3.16) and make various settings based on that. Naming windows (Section 3.17) lets you identify a particular window by reading its environment.
You can also handle some of these cases using the venerable but obscure tset (Section 5.3) program to select and initialize the correct terminal type. Another program that sets the terminal type is qterm (Section 5.4).
Because your terminal type doesn't change after you've logged in, many of these tests probably belong in your .profile or .login file. Those setup files are read when you first log in to a tty. Other tests, especially ones that involve windows, will probably fit better in a per-shell setup file such as .bashrc or .cshrc. Section 3.3 can help you choose.
JP and SJC
If you use several different kinds of terminals (or, as is far more common these days, terminal emulators) and your TERM environment variable is set differently on each terminal, you can add a test like this to your C shell .login file:
switch ($TERM) case vt100: ...do commands for vt100 breaksw case xxx: ...do commands for xxx breaksw default: ...do commands for other terminals breaksw endsw
If you have a Bourne-type shell, use a case statement (Section 35.10) in your .profile instead:
case "$TERM" in
vt100)
...do commands for vt100
;;
xterm)
...do commands for xterm
;;
*)
...do commands for other terminals
;;
esac
JP and SJC
If you log in from other hosts (Section 1.21) or from hosts running the X Window System (Section 24.20), the who am i[5] command will probably show a hostname and/or window information in parentheses:
schampeo@fugazi:1002 $ who am i schampeo ttyp7 Jun 19 03:28 (fugazi:0.0)
(Long hostnames may be truncated. Also, note that some versions of who am i prepend the name of the local host to the username and don't include the remote hostname at all in their output. Check yours before you write this test.) The information in parentheses can help you configure your terminal based on where you've logged in from and/or which display you're using. To test it, add commands such as the following to your .profile file. (In C-type shells, use a switch statement in .login instead.)
case Section 35.10
case "`who am i | sed -n 's/.*(\(.*\))/\1/p'`" in
\(..\) \1 Section 34.11
*0.0) ...do commands for X display 0 ;; mac2*) ...do commands for the host mac2.foo.com ;; "") ...no output (probably not a remote login) ;; *) ...do commands for other situations ;; esac
That uses sed (Section 34.1) to give the text between the parentheses for that remote host to the case. This *0.0 case matches lines ending with 0.0; the mac2 case matches lines that start with mac2; an empty string means sed probably didn't find any parentheses; and the * case catches everything else.
JP and SJC
If you know that certain port (tty) numbers are used for certain kinds of logins, you can test that and change your terminal setup based on the tty you're currently using. For example, some systems use ttyp0, ttyq1, etc. as network ports for rlogin and ssh (Section 1.21), while others use pty0, etc. This Bourne-type case statement branches on the port name:
tty Section 2.7
case "`tty`" in /dev/tty[pqrs]?) # rlogin, telnet: ... /dev/tty02) # terminal on my desk: ... "not a tty") ;; ...not a terminal login session; do nothing esac
In C-type shells, try a switch or if statement instead.
On Linux, you may need to look for patterns to match /dev/pts/0, /dev/pts/1, etc.
JP and SJC
Certain systems set certain environment variables. For example, the X Window System sets a DISPLAY environment variable (Section 35.5). If you've logged in from a remote system using ssh (Section 1.21), look for variables like SSH_CLIENT and SSH_TTY or SSH_AUTH_SOCK on the system you log in to. (If you aren't sure about your system, use the env or printenv command (Section 35.3) to look for changes in your environment at different systems.)
Your shell setup file (Section 3.3) makes decisions based on the environment variables that have been set. Here are examples for both C-type and Bourne-type shells:
[ ] Section 35.26
if ($?DISPLAY) then if [ -n "$DISPLAY" ]; then # on X window system # on X window system ... ... else if ($?XDARWIN_VERSION) then elif [ -n "$XDARWIN_VERSION" ]; then # on MacOS X system # on MacOS X system ... ... else else ... ... endif fi
JP and SJC
Your system may have an /etc/ttytab or /etc/ttys file that lists the type of each terminal port (tty (Section 24.6)).[6] Here are lines from /etc/ttys on a NetBSD system I use:
console "/usr/libexec/getty std.9600" vt100 on local tty00 "/usr/libexec/getty std.9600" dialup off local tty01 "/usr/libexec/getty std.9600" plugboard off local ... ttyp0 none network off ...
For example, port ttyp0 is network, the type used by xterm (Section 24.20), telnet (Section 1.21), etc.
To change your account configuration based on the tty port type, match the first column of that file to the output of the tty (Section 2.7) command, which shows your current tty pathname. The output of tty starts with /dev or /dev/pts. So, to match your current tty to the file, you need to strip the name to its tail. For example, in bash and ksh, these three lines would put the terminal port type (vt100, plugboard, etc.) into the ttykind shell variable:
tty=`tty`
ttytail=${tty#/dev/}
awk Section 20.10
ttykind=`awk '$1 == "'$ttytail'" {print $3}' /etc/ttys`
Then you can test the value with case (Section 35.10) or if (Section 35.13). In C shells, you can set ttytail by using the :t string modifier (Section 28.5) and test its value with switch or if.
JP and SJC
I use several terminal windows of different sizes. I don't stretch the windows after I open them; instead, I set the size as I start each xterm. Here's an excerpt from my X setup file (Section 3.20) that opens the windows:
-e Section 5.22
xterm -title SETI -geometry 80x9+768+1 -e setiathome -verbose -nice 10 & xterm -title "work xterm" -geometry 80x74+329-81 &
The first window has 9 rows (80x9) and the second has 74 rows (80x74).[7] I'd like the less (Section 12.3) pager to use different jump-target lines in larger windows. If the window has more than 24 lines, I want less to use its option -j3 to show search-matches on the third line of the window instead of the first.
On many systems, the command stty size gives the number of rows and columns in the current window, like this:
$ stty size 74 80
Your system might need the command stty -a instead or it could have environment variables named LINES and COLUMNS. We'll use stty size in the following Bourne shell setup file. The set (Section 35.25) command puts the number of rows into the $2 shell parameter. (Using set this way is portable to all shells, but it's a clumsy way to split stty's output into words. If you have a newer shell with array support, it'll be easier.) Then a series of if (Section 35.13)/then (Section 35.26) tests handle different window sizes:
LESS=emqc; export LESS # Put number of rows into $2, configure session based on that: set x `stty size` if [ -z "$2" -o "$2" -lt 1 ] then echo ".profile: bogus number of rows ($2) in window!?" 1>&2 elif [ "$2" -gt 24 ] then LESS=j3$LESS ... fi
Additionally, you may be able to run resize on machines with the X Window System installed; it may output something like this:
schampeo@fugazi:1046 $ resize COLUMNS=80; LINES=37; export COLUMNS LINES;
You may then capture the output and read it for the current setting or simply check the COLUMNS or LINES environment variables.
JP and SJC
I use several xterm windows. Here's an excerpt from my X setup file (Section 3.20):
WINNAME=console xterm -C -title Console -geometry 80x9+0+0 & WINNAME=work xterm -title "work xterm" -geometry 80x74+329-81 &
The WINNAME=name sets an environment variable named WINNAME for the particular command line it's on. This is passed through the environment, through the xterm process, to the shell running inside the window. So the shell's setup file can test for this variable and, by knowing the window name stored in that variable, do specific setup for just that window. For example, in tcsh:
-fSection 11.10, { }Section 28.4
if ($?WINNAME) then
switch ($WINNAME)
case console:
# Watch logs:
tail -f /var/log/{messages,maillog,secure} ~/tmp/startx.log &
breaksw
case work:
/usr/games/fortune
fetchmail
breaksw
endsw
endif
On the console terminal, this .tcshrc file starts a job in the background (Section 23.2) to watch log files. On the work xterm, I get a fortune and grab email from the POP server.
JP and SJC
I work with different types of machines every day. It is often necessary to set things up differently for, say, a Linux box than a SPARCstation or a MacOS X box. Going beyond that, you may want to set things up differently on a per-host basis.
I have this test in my .cshrc file:
setenv Section 35.3
setenv HOST "`uname -n`"
if (-e ~/lib/cshrc.hosts/cshrc.$HOST) then source ~/lib/cshrc.hosts/cshrc.$HOST endif
So, if I log in to a machine named (Section 2.5) bosco, and I have a file called ~/lib/cshrc.hosts/cshrc.bosco, I can source (Section 35.29) it to customize my environment for that one machine. These are examples of things you would put in a .cshrc.$HOST file:
Some machines have /usr/local/bin, and some have /opt. The same goes for cdpath (Section 31.5).
I always like to reach for the upper-right part of a keyboard to erase characters. Sometimes this is the location for the BACKSPACE key, and sometimes it is the DELETE key. I set things up so that I can consistently get "erase" behavior from whatever key is there.
These may be different. You may run a package on a certain machine that relies on a few environment variables. No need to always set them and use up a little bit of memory if you only use them in one place!
In general, this idea allows you to group together whatever exceptions you want for a machine, rather than having to write a series of switch or if statements throughout your .cshrc and .login files. The principle carries over directly to the newer shells as well.
DS and SJC
When you log in to most Unix systems, your shell is a login shell. When a shell is a login shell, it acts differently (Section 3.4).
Sometimes, when you're testing an account or using a window system, you want to start a login shell without logging in. Unix shells act like login shells when they are executed with a name that starts with a dash (-).[8] This is easy to do if you're using a system call in the exec(3) family. These system calls let a C-language programmer give both the filename of an executable file, like sh or /bin/sh, as well as the name that should be used to identify the process (in a ps (Section 24.5) listing, for example), like -sh.
If you're currently using zsh, you can invoke another shell this way by typing a dash and a space before the shell's name:
zsh% - csh
...C shell starts, acting like a login shell...
%
C programmers can write a little program that runs the actual shell but tells the shell that its name starts with a dash. This is how the Unix login process does it:
run_login_csh( )
{
execl("/bin/csh", "-csh", 0);
}
A more general solution is to make a link (Section 10.4) to the shell and give the link a filename starting with a dash. If your own bin subdirectory is on the same filesystem as /bin (or wherever the executable shell file is), you can use a hard link. Otherwise, make a symbolic link, as shown here:
bin Section 7.4, ./- Section 14.13
$ cd $HOME/bin $ ln -s /bin/csh ./-csh
Then you can execute your new shell by typing its name:
$ -csh
...normal C shell login process...
% ...run whatever commands you want...
% logout
$ ...back to original shell
JP and SJC
One way to set defaults for your applications is with environment variables (Section 35.3) that the applications might read. This can get messy, though, if your environment has tens or hundreds of variables in it. A lot of applications have a different way to choose defaults: setup files, similar to shell setup files (Section 3.3). Most of these filenames end with rc, so they're often called RC files.[9] Today's more-complex applications also use their own setup subdirectories. Almost all of these files and directories are hidden (Section 8.9) in your home directory; you'll need ls -A to see them.
This article describes some of the most common setup files. For a more complete list, check your application's manpage:
For the Emacs editor. See Section 19.3.
For the vi (actually, ex) editor. See Section 17.5.
For the GNU Readline library and applications that use it, such as the bash shell.
For the mail (Section 1.21) program and others like it. This can be handy if you use mail from the command line to send quick messages. For example:
# If I send mail to "bookquestions", send it to myself too: alias bookquestions bookquestions@oreilly.com, jerry # When I send a message, prompt me for "cc:" addresses: set askcc
For the MH email system.
A listing of hostnames, accounts and possibly passwords used for connecting to remote hosts with ftp and some other programs. Should have file access mode (Section 50.2) 600 or 400 for security, but this may not be enough protection for passwords! Best used for Anonymous ftp.
For news readers (Section 1.21). (Some newer news readers have more complex files.) A list of newsgroups in the order you want to see them. For example:
comp.security.announce: 1-118 news.announce.important: 1 comp.org.usenix: 1-1745 comp.sys.palmtops! 1-55069,55071 ...
A newsgroup name ending with a colon (:) means you want to read that newsgroup; an exclamation point (!) means you don't. After each name is a list of the article numbers you've read in that newsgroup; a range like 1-55069 means you've read all articles between number 1 and number 55069.
A list of remote hostnames that are allowed to access your local machine with clients like rsh and rlogin (Section 1.21). Remote usernames are assumed the same as your local username unless the remote username is listed after the hostname. This file can be a security hole; make its file access mode (Section 50.2) 600 or 400. We suggest you only use it if your system or network administrator approves. For example:
rodan Allow a user with same username from host rodan foo.bar.com joe Allow username joe from host foo.bar.com
For xauth, a program that handles authorization information used in connecting to the X Window System server.
A resource file (Section 6.5) for the X Window System. Sometimes also called .xrdb.
A shell script (Section 35.2) that runs as you log in to an X Window System session using xinit. (Also see .xsession, later in this list.)
All commands except the last typically end with an ampersand (&), which makes those clients run in the background. The last command becomes the controlling process; when that process exits (for instance, you use the window manager's "quit" command), the window system shuts down. For example:
$Id Section 39.5, exec > Section 36.5, -v Section 35.25, uname -n Section 2.5 , ${..:=..} Section 36.7, export Section 35.3, xrdb Section 6.8, sh -c Section 24.21, exec Section 36.5
#! /bin/sh
# $Id: ch03.xml,v 1.36 2002/10/13 03:50:01 troutman Exp troutman $
# Usage: .xinitrc [DISPLAY]
wm=fvwm2 # window manager
# Put all output into log that you can watch from a window (tail -f):
mv -f $HOME/tmp/startx.log $HOME/tmp/,startx.log
exec > $HOME/tmp/startx.log 2>&1
set -v
# Set DISPLAY from $1 if the X server isn't on same host as client:
if [ $# -gt 0 ]
then
if [ $# -ne 1 ]
then
echo "Usage: .xintirc [DISPLAY]" 1>&2
exit 1
else
DISPLAY=$1
fi
else
host=`uname -n`
DISPLAY=${DISPLAY:=$host:0.0}
fi
export DISPLAY
xrdb -load $HOME/.xrdb
#
# Clients
#
xterm -C -geometry 80x9+0+0 -sl 2000 &
oclock -geometry -1+1 &
xterm -title "SETI console" -bg blue -fg white -geometry 80x9+768+1 -e \
sh -c 'cd /var/cache/seti && exec ./setiathome -nice 5 -verbose' &
# Don't use -e because Mozilla crashes; start by hand from prompt:
xterm -title "Mozilla console" -bg orange -geometry 80x9-0+1 &
xterm -geometry 80x74+329-81 &
#
# Start window manager
#
exec $wm
An executable file (generally a shell script (Section 35.2), but it can be any executable) that runs as you log into an X Window System session using xdm. See .xinitrc, earlier in this list.
Last but not least, your system probably has a lot of setup files in its /etc directory. Look for subdirectory or filenames starting with rc. These are read when your system reboots or changes its runlevel (for example, from single-user mode to multiuser mode). These files are basically shell scripts (Section 35.2). If you know a little about shell programming, you can learn a lot about your system by looking around these files.
JP and SJC
We strongly suggest that you write a manual page for each command that you place in your bin directory. Unix manual pages typically have the following format, which we suggest you follow:
NAME
The program's name; one line summary of what it does.
SYNOPSIS
How to invoke the program, including all arguments and
command-line options. (Optional arguments are placed in
square brackets.)
DESCRIPTION
A description of what the program doesas long as
is necessary.
OPTIONS
An explanation of each option.
EXAMPLES
One or more examples of how to use the program.
ENVIRONMENT
Any environment variables that control the program's behavior.
FILES
Files the program internals will read or write. May include
temporary files; doesn't include files on the command line.
BUGS
Any known bugs. The standard manual pages don't take
bug recording seriously, but this can be very helpful.
AUTHOR
Who wrote the program.
To see how a "real" manual page looks, type man ls.
Feel free to add any other sections that you think are necessary. You can use the nroff -man macros (Section 3.22) if you want a nicely formatted manual page. However, nroff is fairly complicated and, for this purpose, not really necessary. Just create a text file that looks like the one we showed previously. If you are using a BSD system and want your manual pages formatted with nroff, look at any of the files in /usr/man/man1, and copy it.
|
The man (Section 2.1) command is essentially the same as this:
-s Section 11.7
% nroff -e -man filename | more -s
Go to
http://examples.oreilly.com/upt3 for more information on: gnroffawf
You can safely omit the -e option to nroff and the -s option to more, or even substitute in your favorite pager, such as less. And remember that nroff may not be available on all systems, but the web site has gnroff and awf. In fact, on some systems, nroff is simply a script that emulates the real nroff using groff.
Now, you want to make this manual page "readable" by the standard man command. There are a few ways to do this, depending on your system. Create the directory man in your home directory; create the directory cat1 as a subdirectory of man; then copy your manual entry into cat1, with the name program.1 (where program is the name of your special command). When you want to read the manual page, try the command:
% man -M ~/man program
|
Some systems have a MANPATH environment variable (Section 35.3), a colon-separated list of directories where the man command should look. For example, my MANPATH contains:
/home/mike/man:/usr/local/man:/usr/man
MANPATH can be more convenient than the -M option.
|
If you are sharing your program with other people on the system, you should put your manual entry in a public place. Become superuser and copy your documentation into /usr/local/man/catl, giving it the name program.l (the "l" stands for "local"). You may need to create /usr/local and /usr/local/man first. If you can't become superuser, get the system administrator to do it for you. Make sure that everyone can read the manual page; the permissions should be something like this:
% ls -l /usr/local/man/catl -r--r--r-- 1 root 468 Aug 5 09:21 program.l
Then give the command man program to read your documentation.
If you are working on some other systems, the rules are a little different. The organization of the manual pages and the man command itself are slightly different and really, not as good. Write your manual entry, and place it in your doc directory. Then create the following C shell alias (Section 29.3):
less Section 12.3
alias myman "(cd ~/doc; man -d \!$ | less)"
or shell function (Section 29.11):
myman( ) { (cd $HOME/doc; man -d "$1" | less); }
Now the command myman docfilename will retrieve your manual page. Note that if you use a section-number extension like .1, you have to give the entire filename (e.g., program.1), not just the program's name.
If you want to make your manual page publicly available, copy the file into the system manual page directory for section 1; you may have to become superuser to do so. Make sure that anyone on the system can read your file. If the entry is extremely long and you want to save space in your filesystem, you can use the gzip (Section 15.6) utility on your documentation file. The resulting file will have the name program.1.gz; newer versions of the man command will automatically uncompress the file on-the-fly.
ML and SJC
If you're not satisfied with the simple manual pages we discussed in Section 3.21, here's how to go all the way and create a "real" manual page. As we said, the best way to create a manual page is to copy one that already exists. So here's a sample for you to copy. Rather than discuss it blow by blow, I'll include lots of comments (these start with .\" or \").
.\" Title: Program name, manual section, and date
.TH GRIND 1 .\" Section heading: NAME, followed by command name and one line summary .\" It's important to copy this exactly; the "whatis" database (used .\" for apropos) looks for the summary line. .SH NAME grind \- create output from input .\" Section heading: SYNOPSIS, followed by syntax summary .SH SYNOPSIS .B grind \" .B: bold font; use it for the command name. [ -b ] [ -c ] [ -d ] \" Put optional arguments in square brackets. [ input [ output ]] \" Arguments can be spread across several lines. .br \" End the synopsis with an explicit line break (.br) .\" A new section: DESCRIPTION, followed by what the command does .SH DESCRIPTION .I Grind \" .I: Italic font for the word "Grind" performs lots of computations. Input to .IR grind , \" .IR: One word italic, next word roman, no space between. is taken from the file .IR input , and output is sent to the file .IR output , which default to standard input and standard output if not specified. .\" Another section: now we're going to discuss the -b, -c, and -d options .SH OPTIONS .\" The .TP macro precedes each option .TP .B \-b \" print the -b option in bold. Print output in binary. .TP .B \-c \" \- requests a minus sign, which is preferable to a hyphen (-) Eliminate ASCII characters from input before processing. .TP .B \-d Cause daemons to overrun your computer. .\" OK, we're done with the description and the options; now mention .\" other requirements (like environment and files needed) as well as .\" what can go wrong. You can add any other sections you want. .SH FILES .PD 0 .TP 20 .B /dev/null data file .TP .B /tmp/grind* temporary file (typically 314.159 Gigabytes) .PD .SH BUGS In order to optimize computational speed, this program always produces the same result, independent of the input. .\" Use .LP between paragraphs .LP If the moon is full, .I grind may destroy your input file. To say nothing of your sex life. .\" Good manual pages end by stating who wrote the program. .SH AUTHOR I wouldn't admit to this hack if my life depended on it.
After all that, you should have noticed that there are four important macros (listed in Table 3-1) to know about.
|
Macro |
Meaning |
|---|---|
|
.TH |
Title of the manual page. |
|
.SH |
Section heading; one for each section. |
|
.TP |
Formats options correctly (sets up the "hanging indent"). |
|
.LP |
Used between paragraphs in a section. |
For some arcane reason, all manual pages use the silly .B, .BI, etc. macros to make font changes. I've adhered to this style in the example, but it's much easier to use inline font changes: \fI for italic, \fB for bold, and \fR for roman. There may be some systems on which this doesn't work properly, but I've never seen any.
ML and SJC
[1] If you write a csh (or tcsh) script, you probably should use the -f option to keep scripts from reading .cshrc (or .tcshrc). However, you probably shouldn't use csh or tcsh for scripts.
[2] On Mac OS X, /etc is a symbolic link to /private/etc. The actual initialization files for tcsh are in /usr/share/init/tcsh.
[3] ZDOTDIR may be hard to set on your first login when your zsh is a login shell because it's hard to set an environment variable before your first shell starts. (The system program that starts your shell, like login(1), could do the job, I guess.)
[4] Of course, it is all arbitrary and contingent on your keyboard layout and configuration.
[5] Also try "who mom likes" or maybe "who is responsible?" the who doesn't really care, as long as there are only two arguments. So, "who let the dogs out?", as you might expect, causes an error.
[6] Then again, it may not. The RedHat Linux system I tested this on did not; the MacOS X 10.1.5 box I tested it on did.
[7] Both windows have 80 columns. This is a Unix custom that comes from "the old days" when terminals all were 80 columns wide. But it's still a common width today and a good default when you don't need a wider window. Some people are even sort of weird about it, especially for reading email.
[8] bash also has a command-line option, -login, that makes it act like a login shell. zsh -l (lowercase L) does the same for zsh.
[9] Don't ask me why. It's one of those acronyms, like spool (Section 45.2), that's open to interpretation, though one theory is that it is derived from "runcom files," (possibly short for "run commands") on the Compatible Time-Sharing System, c.1962-63 (source: The Jargon File).
| CONTENTS |