Of course, if you're looking for help on the C shell, you may be an absolute beginner, not even sure if you are using the C shell or not. So let's simply remark that, usually, the system administrator for your Unix computer assigns you a default login shell. The shell is a language for issuing commands to the computer. There are several common shells, in particular: the Bourne shell, the C shell, the Korn shell, the T shell, the "Bourne again" shell.
Many features are shared by all the shells. For instance, you always say ls to list your files. But some useful and interesting features are unique to each shell. The comments in this document focus on the C shell; most of what is said here will also apply to the T shell, which is a sort of enhanced version of the C shell.
So, now we wonder, how do you know what shell your system administrator assigned you? The computer wonders about this too. There is a special variable called SHELL, which stores the name of your shell - actually, it stores the name of the program which makes your shell work. So all you have to do is ask for the value of this variable, by typing the following command, which should work no matter what shell you use:
echo $SHELLIf you are running the C shell, the response will be something like
/usr/bin/cshor
/bin/cshThe T shell program name is something that ends in tcsh. But you aren't running the C shell if the answer to the above question is something ending in sh or ksh or bash, for instance.
Your computer account is set up in such a way that you have been assigned a default shell. Whenever you log in, or whenever a program of yours executes, it is the responsibility of this shell to mediate between you and the computer, that is, to interpret your commands and to carry them out.
You can change the shell you are working with, either temporarily, or permanently.
At any time when you are logged in, you can request another shell temporarily, simply by invoking its name. At that point, the shell you were using doesn't terminate, but goes on "hold", and you begin talking to the new shell. When you terminate your conversation with the new shell, you find you are back talking to your original, default shell.
In particular, to start a conversation with a new shell, you essentially simply name the shell, which is the same as asking that program to run. So we might start the C shell by typing
cshalthough it might be necessary or safer to invoke the full path, which might be
bin/csh
To terminate the new shell, you can try some obvious commands like "quit" or "exit"; since you are essentially an input file, as far as the shell is concerned, it is often possible to terminate the shell session using the command "^D", that is, CONTROL-D.
To permanently change your default login shell, use the chsh ("change shell") command, along with the "full name" of the shell program you want. To use the C shell, for instance, you might type
chsh -s /bin/csh
Of course, this command only takes effect when you next log in.
One of the purposes of a shell is to allow you to simplify and customize your working environment. One way that the C shell does this is to set up a number of "environment variables", which record information about the type of terminal you use, the name of the computer you are on, your home directory and so forth.
These environment variables may be set to default values; the shell may have ways of setting them intelligently; and at any time you or any shell script or program your run, can check and change any of these variables. Many programs on a UNIX system will automatically look at the value of some of these variables in order to determine how they should run.
Environment variables are accessible from any UNIX process, program or shell script running under your name. This means that one shell script can set an environment variable, and another shell script can "see" this change. That is because the environment variables are a sort of global variable, visible to you and to all your processes. Their definition stays in effect until all your processes terminate.
By custom, environment variables are usually given names that are ALL CAPS.
A common example is the DISPLAY environment variable, which you may need to set if you are running an X Window program:
setenv DISPLAY mypc.csit.fsu.edu:0.0
The DISPLAY variable tells all X Window programs the name of the computer
on which the graphic information should be displayed.
Another useful variable is called HISTORY. This variable is set to a number, and records how many successive commands should be "remembered". At any time in your session, you can hit Up-Arrow, and review that many of your previous commands. A typical value of this quantity might be 50. If you like this feature, you might even want to increase the value of HISTORY, and you could do that by typing
setenv HISTORY 100
Some variables don't have a value. They just are or are not defined. In such cases, the definition is simpler:
setenv NOCLOBBER
If you want to see the current value of an environment variable, use the dollar sign operator:
echo $HISTORY
To see the list of all your currently defined environment variables, issue the command:
setenv
or
printenv
(Depending on your version of the multifarious UNIX system, one,
both or neither of these commands might work!)
Until an appropriate setenv command is issued, an environment variable is "undefined". If you try to print the value of an undefined variable using an echo command, you are warned that the variable "does not exist". If you would like to check the status of a variable, that is, whether it has been defined or not, you can simply apply the "question mark" operator to the variable, and then ask for its value with a "dollar sign". This returns a value of 0 for "false", that is, "not defined".
echo $?HISTORY
or, if you're writing a shell script, you might print a helpful message:
if ( $?HISTORY )
echo "The value of HISTORY is $HISTORY."
else
echo "HISTORY is not defined."
end
You can change the value of most environment variables at any time using the setenv command; you can also return it to its undefined state using the unsetenv command!
unsetenv HISTORY
Here is a list of some common environment variables. Some of these may be defined on your system. You are free to make up your own variables as well:
setenv HISTORY 50
unsetenv IGNOREEOFand if you really like doing that, put this command in your .login file.
setenv NOCLOBBERthen the shell will not overwrite existing files. In other words, if you already have a file called fred.out, then the command
ls * > fred.outwill fail, because the ls command will not be allowed to overwrite the file fred.out with its output. If you do want to overwrite files but the NOCLOBBER variable is set, you can unset it:
unsetenv NOCLOBBER
setenv PROMPT "> "Others like to know the name of the machine:
setenv PROMPT "$(HOST): "and others want the (often disastrously long) current directory:
setenv PROMPT "$(PWD): "Note that lately, I seem to need a different syntax to modify the prompt command, namely:
set PROMPT = "> "I don't know yet whether I was originally wrong, or there is some intelligible reason for this discrepancy!
cc myprog.c
if ( $status )
echo "The compilation failed."
exit
end
Shell variables are similar to environment variables, except that they are local to the shell script or other process that defines them. By convention, shell variables are given lower case names. A shell variable is defined using the set command. A definition of a variable may include a value, in which case the equals sign is used.
Compare and contrast the following definitions of two shell variables and two environment variables:
set silentmode
set weight = 180
setenv HEIGHT 72
setenv CHILLIN
To understand the difference between a shell variable and an environment variable, begin by defining two variables:
set cash = 15
setenv COW 25
Now suppose we have the following shell script, called "cash_COW.csh":
#!/bin/csh
#
echo "Before: cash = $cash."
set cash = 30
echo "After: cash = $cash."
#
echo "Before: COW = $COW."
setenv COW 50
echo "After: COW = $COW."
To make this script run, type
csh cash_COW.csh
(You can just type "cash_COW.csh" if you have made the script executable
first!) You should notice that cash is not defined at first,
and then it is equal to 30, but that COW has the value that you
assigned it. You were able to use the environment variable to
communicate to the shell script, but the shell variable could not be
used that way.
In fact, the shell script sent you a message back, since it changed the value of COW. Print out the current values of cash and COW and you should see that one changed, and one did not.
Important point: the difference between cash and COW is that one was assigned using set and one was assigned using setenv. That's all that makes the difference between a shell variable and an environment variable.
To see the list of all your currently defined shell variables, issue the command
set
The value of any particular shell variable is found by preceding its name by a dollar sign. You can use the echo command to print out such a value:
echo $term
or
echo "My terminal type is $term."
A shell variable starts out life "undefined". If you want to know whether a shell variable has been defined, use the "?" operator, and precede that by the dollar sign operator. A "0" value means that the variable has not been defined, while "1" means it has been:
echo $?history
At any time, you can change the value of a shell variable using a new set command, but you can also return the variable to the undefined state by an unset command like:
unset pager
It's nice that the C shell environment is customizable, but once you've discovered some settings you like, you don't want to have to type them once more, every time you log in!
The C shell allows you to design three special files, named .cshrc, .login and .logout, in which you can place environment variable settings, print messages or execute programs, automatically.
For these files to be effective, they must reside in your login or home directory. But the ls command will not include them in a usual listing, because their names start with a period, which indicates that ordinarily they are to be "hidden". To see them, you need to include the a (for "all") switch on the ls command:
ls -a
(You will probably be surprised to see 10 or 20 other "dot" files
that are normally hidden. It's a popular way for various programs to
store information from previous sessions, something like Internet cookies.)
The three files have the following distinct uses:
echo "Goodbye!"
just so you know it's there.
Unix commands, programs, and C-shell scripts "talk" to you. You can feed them instructions; they can belch out reams of reports about what they are doing; too often, an unexpected error message brings the conversation to a halt. The C shell provides a number of mechanisms for modifying the way these conversations take place. You may find some of them very useful.
When you run a compiled program and see the words "Hello, world!" on your screen, the program is "writing" to the "standard output device", which, since you are logged in, is your computer terminal. The standard output device is the place that the operating system will send output from:
Perhaps the most interesting thing about standard output is that you can easily save it to a file. This is called redirecting standard output. You simply use the > operator, followed by the name of a file into which you want the output to go:
ls *.f > my_directory.txt
or, if you are running a program with lots of output, and let's
guess the program is called a.out, you can save all that
output in a file called save by a command like
a.out > save
Now it's time to remember that silly noclobber variable. Suppose in the last example that there already was a file called save. Maybe there is important stuff in it. When you type your command, it "overwrites" the old copy of save; it's gone, replaced by whatever the program wanted to write. However, if you have set the noclobber variable, the system won't let you overwrite this file. It will warn you that save already exists, and give you a chance to rephrase the command. If this seems like a good idea to you, first type set to see if noclobber is set; if it is not set, you can type
set noclobber
or put this command in your .login file. On the other hand, if
you can't stand the operating system second guessing you (the way Microsoft
Windows treats users like inveterate idiots), then you may want to issue
the opposite command
unset noclobber
but try explaining that phrase to your Grandma!
The other interesting thing about output redirection is that you can append your output to an already existing file. You do this by using the append operator >>. For instance, the date command prints the date. Let's suppose you type very very slowly, and you want to record, in one file, the date at which you finished each line. Note that the first command creates the file, so it uses the ordinary redirection format:
date > record
date >> record
date >> record
When you get done, the file record will contain three lines.
For another trick you can do with output, see the section on Pipes.
There is a second kind of output channel, usually reserved for error messages, and known as the standard error device. This channel is made available specifically for error messages and other output that should be handled specially.
For instance, if you are running a program that has lots of output, and in the middle of it, an error condition arises, you might want that message to be printed in bold or in CAPITALS or somehow made to stand out, because it may indicate that a serious problem has occurred. Instead, what the C shell offers you is this separate standard error output device.
The standard error device is the place that the operating system will send output from:
Here is an interesting example of the difference between standard output and standard error. Suppose you are compiling a program myprog.C which, sadly, has a number of errors, in fact, thousands of errors. When you type
g++ myprog.C
you get so many error messages that they scroll off the screen.
You'd really like to save them to a file, so you can report them to someone; so you can examine them at your leisure with a text editor; and so you can print them. You try the following command:
g++ myprog.C > messages.txt
but you get the same heap of messages, and the file messages.txt
is empty. That's because the redirection operator > only
redirects standard output; the warning messages from the compiler are
coming out on standard error, and so are not being redirected.
So notice something interesting: by default, your terminal screen is where both standard error and standard output "point". The redirection operator can send standard output elsewhere, but leaves the error output coming to your screen. Actually, that's good to know. It explains how, if you redirect program output to a file, you still get the error messages sent straight to your terminal window where you might be able to do something to fix the error.
In our compiler example, though, we have so many error messages that we really really want them to go to a file too. The way to do this is a modification of the redirection operator that sends both the standard output and the standard error to the same file. You simply append an ampersand to the usual operator:
g++ myprog.C >& messages.txt
Now you might be able to guess correctly that the same trick will work for cases where you are appending the output to an existing file:
g++ myprog.C >>& all_my_errors_so_far.txt
The standard input device is the default channel by which programs and scripts can receive messages from the user. In the simplest model, the program sends prompting messages to standard output (your terminal screen), and you write back answers on standard input (also the terminal screen):
Enter your password:
Shazam
The standard input device supplies data to:
The standard input device can also be redirected. Typically, this occurs when you have prepared a set of input commands to a program, and stored them in a file. You make essentially the same run over and over, but just change one or two values. So it's much easier to use an editor and get it right, than to very very carefully type the data correctly each time.
The input redirection operator is < , as in
a.out < input.data
If you supply more input than the program wants to read, that's no problem. If you don't supply enough, that will cause an error, an "end of file" condition, and the program will terminate. If your input has the wrong format, (you supply characters instead of numbers, for instance), the program will also probably crash.
It's possible, and useful, to redirect both the input and output of a program. In that case, the input always precedes the output:
a.out < input.data > output.txt
Now suppose we have a program called divvy that asks you to give it two numbers, divides the first by the second, and prints the result. A typical execution would look like this:
divvy
Enter the number to be divided:
17
Enter the number that is the divisor:
2
The answer is 8.5
Example 1: if we redirect the output of this program, then here's what we see:
divvy > divvy.out
17
2
and if we print divvy.out we see where the output went:
Enter the number to be divided:
Enter the number that is the divisor:
The answer is 8.5
Example 2: if we prepare an input file called divvy.inp that looks like this:
17
2
then we can redirect the input of the program by a command like:
divvy < divvy.inp
Enter the number to be divided:
Enter the number that is the divisor:
The answer is 8.5
Example 3: Suppose we want to hide all the input and output by typing:
divvy < divvy2.inp > divvy.out
Floating overflow!
Oops! I used the file divvy2.inp, which is slightly different.
It reads:
17
0
and you can't divide by zero, and so we got a message on the screen,
because that's where standard error messages go.
Example 4: So if we really want to see nothing extraneous on our screen, we type:
divvy < divvy2.inp >& divvy.out
and we still get a floating overflow error message, but it will not
show up on our screen, but rather in the output file.
When a program requires input, you have the obvious options of typing that input directly in response to prompts from the program:
fred
Enter the value of N:
1
Enter the value of C:
2
The value of N+C is 3.
A second option is available to you if you know what the input should be. You simply prepare the input in a file, and redirect the input stream to that file:
fred < fred.inp
where fred.inp contains the following lines:
1
2
Suppose, though, that for whatever reason, you do not want to create an input file. A common reason for this might be that you are creating a script to run the program and do other things, and you would really like to have all the commands and inputs visible in a single file. You can do this by essentially creating a temporary file called a "HERE Document". This allows you to enter the input to the program on the subsequent lines of the script. The only requirement is that you redirect the input stream to the document using the special << symbol, with an argument of some quoted string, and that the input be terminated by that string. By tradition, the quoted string is often the word "HERE". This is an example of how you might run the fred program inside a script, with the input to the program also part of the script:
fred << "HERE"
1
2
HERE
Many Unix commands are designed in a common way, to accept input from the standard input device, and to write output to the standard output device. Now if you wanted to apply two Unix commands in succession to a set of data, you could store the results of the first operation in an intermediate, temporary file.
For example, we can use the sort command to sort a list of words, and then use the uniq command to find the unique entries in that list, resulting in a sorted list of unique words:
sort < words.txt > sorted_words.txt
uniq < sorted_words.txt > unique_sorted_words.txt
However, we can eliminate storing the intermediate results by using the Unix "pipe", which takes the standard output of one command and passes it directly to the standard input of the next one:
sort < words.txt | uniq > unique_sorted_words.txt
The sort command reads words.txt, and sends
the sorted data as input to uniq, which outputs the unique
sorted words.
If you're unsure about the difference between pipes and redirection, remember that the pipe symbol connects two running programs, whereas the input and output redirection symbols connect a running program and a file.
Often, you find yourself typing a cumbersome command name repeatedly; or perhaps you expect a command to have one name, but on some computer the command has a slightly different name; or perhaps you always want to include certain arguments as part of a command. In all of these cases, the ALIAS command may be helpful.
The format of the command is
alias shortcut "Quoted string"Once you issue an alias command, you can type shortcut but the operating system will see Quoted string.
On one computer I use, the FORTRAN90 compiler is called fort instead of my preferred name of f90. I solved this by typing
alias f90 "fort"
When I get a directory listing, I always want the "-F" option, and I enforce that by typing first:
alias ls "ls -F"
You can see a list of the aliases you have defined (and some that the system administrator may have already set up for you) by typing alias with no arguments.
The new definitions last only during your current session. To make them automatic, you can include them in your .cshrc file.
When writing a shell script, it is useful to make compound statements that control the execution of other commands. Here are several useful such commands, including if, foreach and while.
Most commands return an error status that can be used to control further actions. This flag is stored in the status variable. For instance, when compiling:
cc -c myprog.c
if ( $status != 0 ) exit
Basically, the argument of the if statement is a number, with 0 acting as false and any nonzero value acting as true.
if ( $status != 0 ) exit
is therefore equivalent to
if ( $status ) exit
and
if ( 0 ) echo "0 trigger"
if ( 1 ) echo "1 trigger"
if ( 17 ) echo "17 trigger"
will print out "1 trigger" and "17 trigger".
You can also use the if...then...endif construction:
cc -c myprog.c
if ( $status != 0 ) then
echo "Something is wrong!"
exit
endif
You can use the if...then...else...endif construction:
cc -c myprog.c
if ( $status != 0 ) then
echo "Something is wrong!"
exit
else
echo "The compilation went OK."
endif
And you can use the more elaborate
if...then...else if...else...endifconstruction:
setenv LANG F90
if ( $LANG == F90 ) then
f90 myprog.f90
else if ( $LANG == CC ) then
CC myprog.cc
else
echo "Unknown language: $LANG."
endif
The FOREACH statement allows you to do simple looping. The format of such a loop is:
foreach var (wordlist)
command1
command2
...
commandn
end
For example,
foreach i (10 20 40)
echo $i
end
The WHILE command allows you to repeat a set of commands as long as some condition is true.
The form of the command is:
while ( condition )
commands
end
A sensible use of this command might involve processing a variable which is actually a vector or list. After we "process" the first item in the list, we can issue the shift command, which discards the first item, and moves the second item to the first position (and all the other items move down one slot, as well). We can then process the new first item, and discard it when done. We can repeat this while the number of items in the list is positive.
Assuming our variable is called list, and recalling that the "#" operator counts the number of entries in a vector variable, our script might look like this:
while ( $#list )
echo "Processing the item" $list[1]
shift
end
The first thing to note is that the find command is not part of the C shell. It's available all over.
The most common thing I need is to perform a GREP on files in all the subdirectories of a given directory. Here's a start:
find . -name '*d.a'
will search all directories from the current one; the test will be those
whose name matches the '*d.a' pattern; the action will be the default one,
which is to print the name.
The C Shell comment character is the "pound sign" or "hash mark", that is, #. If you put this character at the beginning of a line, the rest of the line is ignored.
The semicolon ; can be used if you want to type two (or more) commands on the same line. Thus, you can move to the directory bob, and run a program called fred in a single line:
cd bob; fredThis can be useful if you are writing a program that uses the system routine to issue a command to the operating system. You have to enclose the command in single quotes, and you have to do everything with that one call. So if the program needs to run the fred program, and it's down in the bob directory, your single system command has to do both steps. The semicolon makes this easy:
system ( 'cd bob; fred' );Note that the semicolon at the very end of the system command is simply a line terminator in the C language.
The backslash character \ is usually thought of as an "escape" character, that is, it indicates that the next character is not what you would usually think it to be. In particular, the last character on a line of text is actually the RETURN character. If you are typing a line, and for some reason you want to hit RETURN but you want the operating system to treat this line and the next one as a single line, you can simply but a backslash at the end of the first line. This says "replace the following RETURN by a SPACE". This means you can't break the line in the middle of a word, though!
ls fred bob averylong\ nameis therefore equivalent to
ls fred bob averylong name
The ampersand character & is allows you to issue a command but get a terminal prompt immediately, even though the job is still running. I use this command all the time when I want to edit a file, because usually, I want to edit for a long time, but also have access to the command window. So I am always typing
nedit myfile.txt &This idea is discussed further in the Background Jobs section.
The characters ~, . and .. are used in specifying directories, and will be discussed in the next section.
Moving around the directory structure of a UNIX file system requires the use of the cd command and the address of the place you want to go. The simplest thing to do is to specify the absolute address of the directory you want to reach. Absolute addresses always begin with a /. Thus,
cd /users/8/burkardt/f_src/geometry/examples
Your home directory: When you log in, you start out in your home directory. Usually, your main set of files is stored in various subdirectories of your home directory, and you spend a lot of time going up and down this system.
No matter where you are in the system, you can get back to your home directory by typing the cd command with no explicit destination.
You can also get back to your home directory by evaluating the special environment variable that remembers your home directory:
cd $HOME
and there is also an even shorter symbol ~ that can be used
cd ~
To find out the full address of your home directory, you could type
echo $HOME
or
echo ~
If you have a subdirectory of your home directory called libels,
then the address of that directory can be described by prefixing it
with the ~ symbol:
ls ~/libels
or
cat ~/libels/poison_pen_letter.txt
The ~ symbol is also useful because you can use it to point to the home directories of other people as well. If you know that user joebrown has a file called novel.txt in his home directory, then, if he has set up the permissions correctly, you can type out that file by issuing the command
more ~joebrown/novel.txt
or you can even hop into his home directory and look around:
cd ~joebrown
ls
Your current directory: Your current directory is "where you are now". The UNIX system always keeps track of the directory where you are working. This is called your present working directory. To find out the full name of your present working directory, you can run the pwd command:
pwd
The name of your present working directory is often long and elaborate. The C shell provides two shortcuts. First, there is a variable, called PWD. So you can also find out where you are by asking for the value of that variable:
echo $PWD
The second shortcut for the present working directory is simply ".". So if you don't want to go anywhere, you can say
cd .
and you'll stay right where you are.
Of course, the "." allows you to shorten commands that involving copying or moving files to the present working directory. Instead of typing
cp ~anna/homework/file1.txt /usr/local/burkardt/source
you can simply type
cp ~anna/homework/file1.txt .
(assuming that your present working directory is /usr/local/burkardt/source,
of course!)
To specify the (full) name of a file in the current directory, you can just start with the prefix "./". The time when this comes in handiest is when you're trying to execute a program that's sitting in your current directory. Let's say the program filename is myprog. If you've set up your PATH to include the current directory, then you can execute the program by typing
myprog
but often your PATH is not set up in this way, and so you have to
specify the full pathname of the file. So it's really convenient to
be able to do this using the standard abbreviation:
./myprog
Your parent directory: Your parent directory is the one "above" your present working directory. The C shell supplies a symbolic name for this location, called "double dot" or "..". To move up one level in the directory system, simply type:
cd ..
You can also use this shortcut in order to look at files in another directory at the same level that you are in. That is, let us suppose that your home directory has two subdirectories, called humpty and dumpty. If you are in humpty, and you want to move the file egg from there to dumpty directory, you can type:
mv egg ../dumpty
When you get used to this system, you can start to look at directories that are like "cousins", that is, directories that share a common grandparent:
cp ../../f_src/nag/nag.html .
says to go up to the grandparent of the current directory, and
then back down to the /f_src/nag directory from there.
To determine if a variable has been defined, use the question mark:
echo $?SHELL
a value of 0 means it has NOT been defined, a value of 1 means it
has been defined.
Assuming a variable has been defined, you can see its value with the echo command:
echo $SHELL
reports the name of your shell program. This variable is automatically
defined by your computer system.
If you use the $ operator on a variable that is not defined, you get an error message telling you the variable is not defined. That is OK if you are a human being, but it confuses a computer program or shell script. So if you are a shell script, and you aren't sure whether a variable has been defined, it pays to use the ? operator first.
If the variable BOB is defined, then the expression ?BOB has the value true (that is, 1). If you want to know whether BOB has been defined, you want to know the value of ?BOB, that is, you are asking for $?BOB.
Maybe this will be more clear with the following example, which wants to know the current default printer. Usually this is simply $PRINTER, but maybe that variable hasn't been defined. In that case, we want to define it to be an "epson" printer. Here's how you do that:
if ( $?PRINTER ) then
echo "Current printer is " $PRINTER
else
set PRINTER = epson
endif
As you've probably figured out, if a variable has been defined, you can see its value with the echo statement. For instance
echo $SHELL
reports the name of your shell program.
In a very few cases, a shell variable may be a "vector" or list. A particular example of this is the ARGV variable, which contains the command line arguments associated with the invocation of a C shell script. It is also possible to assign a list of values to a variable by enclosing the values in parentheses:
set chores = ( laundry cooking bedmaking )
For vector variables, it is important to know how many entries are in the list. This is done using the # operator. Thus, #ARGV is the number of command line arguments, and #chores is the number of chores. To print out or manipulate this value, you have to use the $ operator. In particular, you could report the number of chores by the statement
echo "Today I have " $#chores " to do."
The individual values in a vector variable can be addressed by indexing. Thus, the first chore is chore[1].
The simplest way to run a program, is to type its name. If your program is an executable created by a compiler, then by default it will be called a.out, and so many people run a program by typing
a.out
For many reasons, it's likely that the first time you try to do this, nothing will happen. Well, actually, it's likely that what will happen is that you will get the following message:
Command not found.
Generally, each "sentence" that you type to the C shell is regarded as an instruction to do something, with the first word of the sentence being the name of a command. The C shell maintains a list of the commands it knows, and if the first word of your "sentence" is not on the list, the shell tells you it can't find the necessary command.
Now, the C shell will always allow you to use a new command, as long as you tell it exactly where to find it. So, you can run the program a.out if you specify its exact location. Well, normally, you would not want to type the long list of directions. But luckily, your present working directory is probably where the program is right now; in that case, you can type
./a.out
This means that you can run any program anywhere, if you are able to point to it. You may even be able to run a program that is in someone else's directory, as long as you have the appropriate file permissions. For instance, you might be able to run the the quote program in my account by typing
~burkardt/bin/Alpha/quote
A C shell script can also be regarded as a program. You run it in the same way, by typing its name. There's no default name for such a script, but there is a default filename extension of csh.
The safest way to run a shell script is to invoke the C shell, and use the script as input to it:
csh my_job.csh
The less "safe" way to run a shell script is to treat it as a program:
my_job.csh
This won't work unless several things are true:
A shell script is a very nice way to put a bunch of commands together that you always have to do, or a good place to stick commands that have long complicated names in them. For example, here is one of my shell scripts that compiles a program, links it to a library, and runs it. The basic structure is:
#!/bin/csh
f90 -c divdif_prb.f90
f90 divdif_prb.o -L$HOME/lib/$ARCH -ldivdif
a.out > divdif_prb.out
but the full blown script is
#!/bin/csh
#
set echo
#
f90 -c -fast divdif_prb.f90 >& compiler.out
if ( $status != 0 ) then
echo "Errors compiling divdif_prb.f90"
exit
endif
rm compiler.out
#
f90 -fast divdif_prb.o -L$HOME/lib/$ARCH -ldivdif
if ( $status != 0 ) then
echo "Errors linking and loading divdif_prb.o"
exit
endif
rm divdif_prb.o
#
mv a.out divdif_prb
./divdif_prb > divdif_prb.out
if ( $status != 0 ) then
echo "Errors running divdif_prb"
exit
endif
rm divdif_prb
#
echo "The divdif test problem has been executed."
The first line of a C shell script must be "#!/bin/csh". This tells the operating system what shell to run when interpreting the commands in the file. (You could have specified the Bourne shell, the Korn shell, or other shells instead of the C shell, and then your shell file would have to follow that syntax instead.) On this first line where you invoke the shell, you can also include an argument.
One argument is "-x", which will cause the shell script to print out each statement as it executes it. To be clear, you would begin your shell script with the line "#!/bin/csh -x" to get this behavior.
The command list is set up by checking for executable programs in a special list of directories where the C shell expects commands to be stored. Common directories for commands maintained by the operating system include
/bin
/usr/bin
/usr/local/bin
There may be other places that your system has been set up to check.
To find out the current list of such directories, type
echo $PATH
To make your life easier, you can define your own directory where you put executable commands you want to use from time to time, and you can also request that your current directory (whatever it happens to be) should be considered a possible location for commands.
To do this, you need to modify your PATH, using a command like
set path = ( $path $HOME/bin . )
in which case, the C shell will check its normal path, but then
also look in your bin directory, and in your current directory,
whenever you type a command.
Another reason that a program won't run is if you just created for the first time in this login session, or moved it, or renamed it. Then, even though it may be in your path, the system may not know that. The system only actually only checks what's in your path when you log in! You can force the system to "look again" by typing rehash.
Normally, when you start a job, it's in the foreground. You can't do anything in that session until the job terminates. In some cases, you'd like to start the job, and immediately be allowed to do other work, in the same session or window. The easiest way to do this is to append an ampersand to the command.
For instance, I might want to edit this document with the nedit editor, but also want to be able to issue commands from the command line to test the suggestions I am making. So I type
nedit c_shell.html &
and an edit window pops up, but I also get a new command prompt,
so I can work in the edit window or the command window.
It's easy to deal with a window editor session that has been put into the background, because you still see the editor window and can interact with the job. But another common situation is that you have a computational program that takes a long time to run. You want to start the program, and then continue work. Let's call this program king. To start this program, but put it immediately into the background, type:
king &
Notice that the operating system will print out something like the
following:
[1] 13788
The first number indicates that this is your first job in the
background. The second number is a PID or "process id",
a number you can use to identify the job later, if necessary.
Suppose, an hour later, you wonder whether king is still running. You can simply use the ps command to get your "process status". Every process associated with you will show up in the list, along with a number. For instance, you might see this:
PID TTY TIME CMD
13462 pts/1 00:00:05 tcsh
13788 pts/1 00:01:23 king
13791 pts/1 00:00:00 ps
This means that process 13462 is your login session, which is really
running tcsh. And there is process 13788, your king
program, still running. Finally, even the ps command you
just invoked shows up with a process identifier.
If you put a job into the background, you can bring it back to the foreground using the fg command. If, for instance, the king program is printing out messages saying that iteration 1 converged, iteration 2 converged, and so on, you don't see any of that as long as it is in the background. But you could "peek" at the output by moving the job to the foreground:
fg
iteration 1977 converged.
iteration 1978 converged...
Oops! Now we have the job in the foreground, and we've lost
control of the terminal. We found out what the job was doing,
but can we put it back into the background? Yes! First type
Control-Z (Control Key + Z) to stop the job. Then type
bg
to move it into the background.
What if you actually don't want the job to continue? There are several ways to kill a job. If the job is currently the foreground job, you can kill it by typing Control-C. Otherwise, if the job is in the background, you can kill it by specifying its PID:
kill 13788
(Sometimes, if the kill command doesn't work at first, you
can try the kill -9 command, which tries a little harder!)
You might think using the ampersand or the bg command is a great way to run a job "in batch". That is, if you want to run a program that will take hours to finish, just type
a.out &
and then log out. But this will not work! When you log out, all
processes associated with your session are terminated, included the
job you were running in the background.
The same problem occurs if you are on your workstation, and you want to log into a remote computer and start a job. You can use the ampersand to start the program running in the background, but now you have to stay logged in to the remote computer until your remote job finishes. If your workstation crashes, or your connection to the remote computer is interrupted, then your background job is killed, even though it is "perfectly healthy" and could continue to run. The rule is, a background process is killed when the "parent" process dies. And the parent process is your login session.
Fortunately, there is a way around this problem. If you really want to set up a job, start it running, and require that it continue to run, even if you log out, you need to use the nohup ("no hangup") command. The syntax is a little confusing, but basically, your command line starts with the word nohup, then is followed by the program you want to run, including any I/O redirection, and finishes with an amperand. One version of this command might be
nohup sandman &
and a fancier version would be
nohup sandman < input > output &
Even though a nohangup job is "divorced" from your login session, you can still keep an eye on it with the pscommand, and you can kill the job at any time with the kill command.
One more thing: if you put big jobs on an interactive computer in this way, you may become unpopular! The big jobs may bog the computer down, in part because they are treated as just as important as any interactive session. It's really more appropriate to give big background jobs a slightly lower priority. You can do this with the nice command. Doing this will only slow your program down a small amount, but it will allow you and other interactive users to happily get prompt response from the editors, browsers, and compilers that you want to work with while waiting for your big jobs to finish!
Simple examples of the use of the nice command with a job to be run in the background and in no-hangup mode include:
nice nohup sandman &
nice nohup sandman < silica > limestone &
You can return to the HTML page.