A shell is simply a process, and any command you run is executed on your behalf by the shell. So, let's start with what a process is.
Every time you issue a command, the UNIX operating system starts a new process and suspends the current process (the C-shell) until the new process completes. For example, consider compiling a program. When you type
cc program.cyou cannot issue other commands in that same window (or to that same shell) until the compilation has completed. The C-shell is waiting for cc(1) to finish before continuing; we say that the compiler is executing or running in the foreground. If we tell the shell to continue to accept new commands even while the compiler is running, we say that the compiler is executing or running in the background. In the sections below we will discuss how to cause jobs to run in the background.
Associated with each process is a unique Process Identification Number, or PID, which is assigned when the process is initiated. When we want to perform an operation on a process we usually refer to it by its PID.
ps -xtells the system to list all your jobs currently running on the machine that you are logged in to:
% ps -x PID TT STAT TIME COMMAND 6799 co IW 0:01 -csh (csh) 6823 co IW 0:00 /bin/sh /usr/bin/X11/startx 6829 co IW 0:00 xinit /usr/lib/X11/xinit/xinitrc -- 6830 co S 0:12 X :0 6836 co I 0:01 twm 6837 co I 0:01 xclock -geometry 50x50-1+1 6841 p0 I 0:01 -sh (csh) 6840 p1 I 0:01 -sh (csh) 6842 p2 S 0:01 -sh (csh) 6847 p2 R 0:00 ps -xThe meaning of the column titles is as follows:
PID | process identification number |
TT | controlling terminal of the process |
STAT | state of the job (see below for more information) |
TIME | amount of CPU time the process has acquired |
COMMAND | the command that the process represents |
R | Runnable processes |
T | Terminated (stopped) processes which can be restarted |
P | Processes waiting for pages to be swapped in r out |
D | Processes in non-interruptible waits; typically short-term waits for (disk or network) I/O |
S | Processes sleeping for less than about 20 seconds |
I | Processes that are idle (sleeping longer than about 20 seconds) |
Z | Processes that have terminated but whose parents have not yet been notified (zombie processes) |
C Shell distinguishes between the two very simply. To set a local environment variable called THISVAR to the value 12345, just say
set THISVAR=12345If you run a subprocess, this value will be invisible to the subordinate processes:
% set THISVAR=12345 % echo $THISVAR 12345 % csh start a subshell % echo $THISVAR THISVAR: Undefined variable.On the other hand, if you want to make THISVAR global (or, as is sometimes said, make it exportable, or visible to subprocesses, or inherited), use the setenv command:
setenv THISVAR 12345Note there is no equals sign. Now:
% setenv THISVAR 12345 % echo $THISVAR 12345 % csh start a subshell % echo $THISVAR 12345To clear a shell variable, so it is undefined, say
unset THISVARif it is a local environment variable or
unsetenv THISVARif it is a global environment variable.
One last point: there's no way for a subprocess to alter the environment of a parent (or any ancestor) process. So, if you try to set something in a subshell, for example, it won't affect anything in the parent shell:
% setenv THISVAR 12345 % echo $THISVAR 12345 % csh start a subshell % echo $THISVAR 12345 % setenv THISVAR "hello, world" % echo $THISVAR new value in subshell, but ... hello, world % exit go back to the parent shell % echo $THISVAR the original value is intact 12345Here are some useful built-in shell variables. For a few, the issue is whether or not they are set; the specific value is unimportant. In this case, just say
set VARIABLEor
setenv VARIABLEdepending on whether you want it to be inherited.
home The user's home directory.
ignoreeof If set (the value is unimportant), you must type logout or exit to terminate the shell. If not set, typing ^D will also terminate the shell. This is a godsend to lousy typists like me.
mail Set this to a list of files to watch for new mail; if the first word is an integer n, check for mail once every n seconds. For example,
set mail=(300 /usr/spool/mail/account /usr/msgs)will make the shell look for changes in the file "/usr/spool/mail/account" and in the directory "/usr/msgs", and report that
New mail has arrived in filewhere file is the appropriate file (or directory) name.
path Set this to a list of directories to search for executables. When you give a command that does not contain a '/' character, the shell looks in each directory in this list for an executale file with the name you typed. It checks the directories in the given order, and stops when it has found the first such file.
prompt Set this to what you want the shell to prompt you with. The default is '% '; an exclamation point '!' in the value is replaced by the current command number, which is very useful for using the history mechanism. Thus:
% set prompt="\! % " 34 %(You need the backslash to keep the shell from interpreting the '!' as a history character in the set command. Read on!) By custom, if working as the superuser, the prompt includes the sharp sign '#' instead of the percent '%'.
shell The absolute path name of the shell you are executing.
status When a UNIX command or program exits, its success or failure is indicated by its exit status code. If a command is successful, by convention its exit status code is 0; if not, the exit status code is nonzero. This code is stored in this variable, which is reset after the execution of each command. So, if you want to see the exit satus code of a command, do it like this:
% pwd /usr/bin % echo $status 0time If this is set to n, then all commands which take over n seconds of CPU time to execute will have statistics printed. By default, these are (in order):
a.out < inputfile > outputfile &will execute my compiled program while allowing me to submit other commands for execution while it is running. A good example of an instance when we would like to execute a job in the background is when we start a window during a X session. We would like to start the window from an existing window, but we still want to use the original window. We execute the command
xterm &and this starts a new window while allowing us to keep using the current window. When you do this, the shell will tell you the PID of the command after the job starts:
% xterm & [1] 14638means that xterm has started and its PID is 14638. The ".X11Startup" file that you have on your account contains lines like the following:
dxclock -geom 144x66-0+0 & xterm -geometry +1050+70 -fn 9x15 -fg white -bg black & xterm -geometry +450+450 -fn 8x13bold -bg white -fg black & xterm -geometry +1050+370 -fn 9x15 -fg white -bg black & xload -geom 300x120-0-0 -bg Red - fg White -update 5 -scale 2 &The middle three lines tell the shell that you want three xterm(1) processes (with more specific information being given by the options) and that they should be run in the background. The first and last line cause the clock and the load indicator to be displayed, respectively ,and that they should both be run in the background.
If we start a job and we decide that we want to move it to the background there are a number of ways to do it. The simplest way to move a job to the background is to use ^Z (control-Z) in the window that the job is executing, which will suspend the job (not to be confused with ^C which kills the job). The message Stopped will appear on the screen. We then resume the job in the background with the 'bg' command. For example, we want to compile the program "bigprog.c" and we know that it will take a long time and we do not want to wait for its completion. We type the command
gcc -ansi bigprog.cbut we forget to append the ampersand. We type
^Zto suspend the job and then type
bg %gccwhich resumes the job in the background, allowing us to submit other commands while the compilation takes place. The shell will tell us when the background job has completed with a statement like
[1] Done gcc -ansi bigprog.cNormally, this message will appear only when the shell is ready to prompt you for another command. If you want it to appear whenever a background job terminates, whether or not the shell is ready for you to type another command, use the shell variable
notify Set this if you want the shell to notify you immediately upon the completion of a job rather than waiting until just before prompting.
A job can be moved into the foreground with the fg command. For example, if we wanted to resume the compilation of our program after we suspended it with ^Z we could use
fg %gccwhich would resume the job in the foreground (we would have to wait for it to complete before issuing other commands).
If we want to stop a job in the background without moving it to the foreground, we can type
tostop %gccand that would stop the job. We could resume it by either moving it to the background with bg or to the foreground with fg.
When we run a job in the background the output will still come out on the screen as if we ran it in the foreground. There are two ways to handle this. issuing the command
stty tostopwill cause any background process to block before generating output to the screen; you will be told using the notification mechanism described earlier in this section. You can then move the job into the foreground. As an alternative, you can use redirection.
command > outputfileIf outputfile does not exist it will be created. If it does exist, what happens depends on the setting of an environment variable:
noclobber If set and output is redirected to an existing file, the command is not executed and an error message is given. If set and the file does not exist, it will be created. If this variable is not set, the output will be sent to outputfile (erasing it if necessary).
If you append output to a file using
command >> outputfilethe sense of the noclobber variable is reversed; if noclobber is set, then you will get an error message if outputfile does not exist; but if outputfile does exist, you will not get an error and the output will be appended to it. If noclobber is not set, outputfile will be created if it does not exist.
In any case, if noclobber is set and you want the shell to pretend it isn't for one command, append the exclamation point '!' to the redirection:
command >! outputfileoverwrites outputfile whether or not noclobber is set, and
command >>! outputfilewill create outputfile if it does not exist. Notice there is no space between the '>' and '!'.
Input can also be redirected; for example,
wc < inputfilewill cause wc to act as though the contents of inputfile were typed at the keyboard.
In addition to redirecting the standard output (called stdout) of the background process we must also redirect any error messages to a file. Error messages are sent to stderr (standard error), normally stderr is sent to the screen so that you see the error messages as they occur. The following syntax is used to redirect the stderr information to the same file that we redirect the standard output to
somecommand >& outputfileWe can combine it with input redirection, too:
somecommand < inputfile >& outputfilecauses all output from somecommand to be written to the file outputfile. Note that somecommand could be a UNIX command, "a.out", etc. This construct also responds to the setting of noclobber, and that can also be overridden by placing an '!' right after the '&', with no intervening spaces.
A final type of redirection is the pipe. A pipe connects the output of one process to be the input of another. For example,
date | wctakes the output of the command date(1) and feeds it into the standard input of wc(1). As another example,
ps | head -4prints the first four lines of the process status listing.
xterm &and later decide to get rid of it. We find out the PID (for example, by using ps -x); call this pid. Then we type
kill -9 pidand the window will disappear. The option -9 ensures that job/process will be killed. A simplier way to kill a process that you are running in the foreground in a window is to type ^C in the window that the job is running in. Next time you log in try starting up a new window in the background and then killing it with the commands given above.
ls chap*lists all files in the current directory which begin with the letters "chap". The `*' metacharacter means to match 0 or more ordinary characters.
Other metacharacters are '?', which means match one character; so the pattern
a?cwould list the files "abc", "acc", and "adc", but not the file "abbc". The characters '[' and ']' delimit a range, so the pattern
a?c[0123]matches the files "abc1", "acc3", and "axc1", but not "abbc1", "abc4", or "azc9". You can use a hyphen '-' to indicate a contiguous range, so the above pattern could also have been written
a?c[0-3]Be careful, though; the pattern "[A-z]" matches all the letters and several other characters, too, because the range is over the ASCII character set!
The pattern matching matches file names; if no files matching the pattern exist, you get an error message
% echo ab*ef No match.To change this behavior, you can use a shell variable:
nonomatch If set, return the filename substitution pattern rather than an error if the pattern is not matched. Note that malformed patterns (for example, leaving a closing ']' out) will still result in errors.
You can also disable these metacharacters by setting another shell variable:
noglob If set, inhibit filename substitution; the metacharacters lose all special meaning. This is most often done in shell scripts or startup files when the character strings involve the metacharacters, but require them to be interpreted as regulatr characters.
If you just want them disabled for one command, you can put a '\' in front:
% echo a* ab ac ad % echo a\* a*Two other metacharacters deserve mention. The character '~' expands either to your login directory (if it is followed by a '/') or to the home directory of the named user, if he or she exists (if the tilde is not followed by a '/'). So,
~/.cshrcrefers to your ".cshrc" file, and
~bishop/.cshrcrefers to my ".cshrc" file. Also, the characters '{' and '}' enclose alternate substitutions; for example,
% echo ab{c,d,e}f abcf abdf abefUnlike the pattern matching metacharacters, there need be no files "abcf", "abdf", or "abef".
% history this shows your last few commands and event numbers 131 date 132 vi write.c 133 gcc -g write.c 134 write root 135 diff write.c oldwrite.c % !da repeat the last command that began with a 'da' Tue Jan 18 11:58:01 PST 1994 % !! repeat the last command Tue Jan 18 11:58:30 PST 1994 % !132 repeat command number 132 ... vi write.c ... % !gcc:s/-g/-O/ repeat the last gcc command, but replace -g with -O % !?root? repeat last command containing "root" anywhere (here, 134) % mail root < !vi:$ mail root the file last edited with vi % ^root^admin^ same as !!:s/root/admin/ % mv write.c chat.c % mv oldwrite.c oldchat.c % !diff:gs/write/chat/ diff the new file names; g means global (everywhere); default is to replace first match % !{diff}old rerun the last command, appending "old"; without the curly braces it is read as !diffold, which you didn't meanFour variables control how the history is handled and saved:
histchars Set this to a string, the first character of which indicates history substitution (and by default is '!') and the second of which indicates quick history substitutions (the default is '^').
history Set this to the number of commands you want the shell to remember.
savehist Because environments are not preserved when a process terminates, your history gets lost whenever you log out. Set this variable to the number of commands to save across invocations; that many commands will be saved in the file ".history" in your home directory, and when you start a new shell, those commads will be read in.
verbose Print the command after history substitution is completed. This helps keep down the aggravation, because you can see what the shell thinks you told it to do.
alias ls ls -FWhen you type the command ls(1), the shell looks at the first word in the command (if it were part of a pipeline, the shell would look at each command in the pipeline) and sees if it is an alias. If so, it replaces the alias with its definition and repeats the process unless the first word of the new command is the same as the first word of the old one (this prevents obvious loops). So, after the above command, the command "ls -a" would be replaced by "ls -a -F" and that executed.
You can use the history mechanism to pull out parts of the command after the aliasing is applied. In this case, the old (pre-alias) command is treated as the previous command. So:
% alias lookup grep \!^ /etc/passwd % lookup root root:abcdefghijklm:0:1:Charlie the Root:/:/bin/cshNote the backslash '\' in fromt of the exclamation point '!'; this is needed to prevent the shell from treating !^ as a reference to the previous command, as history substitutions are done before alias substitutions.
touch 'this file has 8 words in its name'Strings enclosed in back quotes '`' are treated as shell commands; they are executed, and the string is replaced by the output. In the output, each contiguous sequence of blanks, tabs, and newlines is replaced by a single blank, so (for example):
% cat XYZ hi there goodbye % echo `cat XYZ` hi there goodbyeStrings enclosed in double quotation marks '"' have any shell variables replaced by their value, history substitutions done, and any commands in '`' executed as described above; otherwise, the string is left alone. For example,
% echo "$user files???d !32" bishop files???d vi pwd.cNote the filename pattern was not expanded, but that history substitution was done and the shell variable was evaluated.
Here is a sample ".cshrc" file:
set path=( /bin /usr/{bin,hosts} /usr/pkg/*/bin .) alias term 'tset -s -n \!* > /tmp/t$user; \ source /tmp/t$user;\ /bin/rm /tmp/t$user' set history=50 set noclobber ignoreeof echo "Welcome to `hostname`, your friendly UNIX host\!"The first line sets the search path; notice the use of file name metacharacters. The second line (which continues onto the third line) sets up an alias to run the program tset(1), which resets the terminal modes (use it if you think your screen is messed up; a good clue is if pico(1), pine(1), or vi(1) refuses to work in anything other than open mode). The third line says that the shell is to remember the last 50 commands, the fourth line sets two options discussed earlier, and the fifth line names the host you're on as it greets you.
The ".cshrc" file contains aliases and other things to be executed whenever a C Shell starts. For things like initial terminal setup or setting global environment variables, the ".login" file is more appropriate, becasue it will only be executed once during the session:
setenv EXINIT 'set ai' term -Q - -m 'switch<=1200:?vt100' -m 'switch>=2400:?bg' $TERM set term=$TERMThe first line sets an environment variable used by vi; whenever vi starts, autoindent mode is on. The second line uses a horrible program, tset; it basically says that if the speed of the terminal is1200 baud or less, ask if the user is using a VT100; if it's 2400 baud or more, ask if it's a Bitgraph; but if the user has already identified a terminal (as is done with network logins), don't ask about anything. (You don't need to be able to understand that line, but look in the manual page, or ask an experienced user, if you want to.) Then the C Shell variable term is set to the terminal type.
Department of Computer Science
University of California at Davis
Davis, CA 95616-8562