Using the C shell


TABLE OF CONTENTS


Am I Using the C Shell?:

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 $SHELL
If you are running the C shell, the response will be something like
/usr/bin/csh
or
/bin/csh
The 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.


Changing Your Shell:

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.

Temporarily Changing Your Shell:

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

csh
although 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.

Permanently Changing Your Shell:

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.


Environment Variables:

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:

AUTOLOGOUT
If this variable is set, it is equal to the number of minutes of inactivity before your session will automatically be terminated.
ARGV
is a list of all the command line arguments specified when the current C shell was invoked. This is usually not of interest to you, the living person, but it is of interest if you are a C shell script. In that case, there are commands that allow you to retrieve, one at a time, the command line arguments.
DISPLAY
the DISPLAY variable is used by the X window system.
EDITOR
the name of your preferred editor program.
GROUP
the UNIX group to which you belong. People in the same group are often allowed to share files or run each other's programs, while this might not be true for general users.
HISTORY
the number of your commands that the shell will remember. You can review these commands by hitting the Up-Arrow key. To set the number of commands, do something like
setenv HISTORY 50
HOME
this variable records your home directory, where you login, and where you are returned when you type "cd" with no arguments.
HOSTNAME
this is the "name" of your computer, often the first part of the ftp address.
IGNOREEOF
this is an unusual variable, which does not get a value. It is either defined or not defined. If this variable is set, you cannot log out using a Control-D. If logging out by typing Control-D makes you happy, then issue the command
unsetenv IGNOREEOF
and if you really like doing that, put this command in your .login file.
LOGNAME
this variable is the login name of the user.
NOCLOBBER
this is an odd variable. It doesn't get a value; it's simple defined or not defined. If it is defined (by the command
setenv NOCLOBBER
then the shell will not overwrite existing files. In other words, if you already have a file called fred.out, then the command
ls * > fred.out
will 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
PATH
This is the list of directories (separated by colons) that will be searched when the user types a command word. The shell will search these directories in this order until it finds an executable program or shell script that matches the command word. It is often helpful to modify the PATH to include your current directory ("."), or perhaps a "bin" subdirectory of your home directory ("$(HOME)/bin").
PRINTER
this variable may be given the value of your default printer.
PROMPT
This is the string that will be printed out as a prompt. Some people like their prompt to be minimal:
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!
PWD
this variable, the "present working directory", is controlled by the operating system, not you, and is updated as you move around.
SHELL
your shell. If you are living right, this should be something like "/bin/csh" or "/bin/tcsh".
STATUS
this variable records the "status" of the last command executed. If you compile a file, the compile statement returns a status that is "true" if there is an error. If you want to print a message when compilations fail, you could do that this way:
            cc myprog.c
            if ( $status )
              echo "The compilation failed."
              exit
            end
          
TERM
this variable may have a value recording your terminal type, such as "vt100".
USER
this variable is the login name of the user.
VISUAL
this variable is the name of your preferred "visual" editor, that is, a screen editor, such as the horrid "vi".


Shell Variables:

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
      


Customization Files:

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:


Input, Output, and Error Messages:

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.

Standard Output

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.

Standard Error

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
      

Standard Input

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
      

A Quadruple Example

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.


Input from HERE Documents:

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
      


Pipes:

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.


The ALIAS Command:

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.


Control Statements:

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.

The IF statement:

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...endif
construction:

        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:

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 statement:

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 FIND Command:

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.


Special Characters:

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; fred
This 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\ name
is 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.


Special Places:

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.


Symbolic Names:

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].


Running Programs:

Starting a Program

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
      

Starting a Shell Script

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.

Setting Your Path

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.

Background Jobs

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!)

Nohangup Jobs

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.


Last revised on 02 September 2007.