0. Table of Contents
1. Working With Variables
2. Invoking Shell Scripts
3. Conditional Statements
4. Iteration Using Loops
5. Command Substitution
6. Advanced Topics
1. Working With Variables
% set var = one
% echo $var
one
% set n = 1
% echo $n
1
What can be done can also be undone. Use 'unset' to remove any
values (bindings) associated with a variable name.
% unset n
% echo $n
csh: n: Undefined variable.
Lists (sometimes referred to as arrays) have special syntax.
% set lst = ( one two three four )
% echo $lst
one two three four
% echo $lst[1]
one
% echo $lst[2]
two
% echo $lst[4]
four
You can also reference subsequences of a list.
% echo $lst[2-3]
two three
But you can't reference elements that don't exist.
% echo $lst[5]
lst: Subscript out of range.
The syntax $#name refers to the number of items in a list.
% echo $#lst
4
% echo $lst[$#lst]
four
You can use 'set' to change the elements in a list.
% set lst[4] = five
% echo lst[4]
five
You can use 'shift' to shift all the items in a list to left
thereby eliminating what was the first item in the list and
reducing the number of items in the list by one.
% shift lst
% echo $lst[1]
two
% echo $lst[4]
lst: Subscript out of range.
% echo $#lst
3
Use '@' instead of 'set' if you want to perform some math on
the right hand side of the '=' sign. All math calculations
operate only on integers and return only integers.
% @ n = 3 * 4
% echo $n
12
You can use parentheses to group operations but leave space between
operators and operands. If in doubt add spaces; in most cases it
won't hurt.
% @ n = ( 3 * ( 4 + 5 ) )
% echo $n
27
Note that a variable name appearing on the left hand side of an
assignment ('=') doesn't have a preceding '$' but variables
appearing on the right hand side must be preceded by a '$'.
% @ n = 2 + 3
% @ n = $n + 1
% echo $n
6
% @ m = 2 * $n
% echo $m
12
Referencing a variable is one place where adding space can cause
problems. The following is probably not what you intended.
% echo $ n
$ n
Note that integer division always rounds down.
% @ n = 5 / 3
% echo $n
1
You can use the modulus operator '%' to determine the remainder
after division.
% @ n = 5 % 3
% echo $n
2
There are several convenient shorthand operators.
% @ n = 1
% echo $n
1
% @ n ++
% echo $n
2
% @ n += 5
% echo $n
7
% @ n *= 5
% echo $n
35
Here are some special variables worth knowing about: 'cwd' is the
current working directory, 'home' is the user's home directory, 'path'
is the sequence of directories that the shell traverses when searching
for a program, and 'shell' is the currently executing shell program.
% echo $cwd
/u/tld/tmp
% echo $home
/u/tld
% echo $path
/bin /usr/bin /usr/local/bin /u/tld/bin .
% echo $shell
/bin/csh
The variable 'argv' is available only inside a shell script where it
is bound to the list of words (arguments) that appear on the command
line to the right of the name of file containing the shell script.
The following assignments should be self explanatory.
set list_of_args = ( $argv )
set first_arg = $argv[1]
set second_argument = $argv[2]
set number_of_args = $#argv
There are also short variable names that you can use to refer to
the arguments appearing on the command line: $1 refers to the first
argument, $2 to the second argument, and so on. In addition, $0
refers to the file of commands (script) to be executed.
set command = $0
set first_argument = $1
set second_argument = $2
2. Invoking Shell Scripts
When writing a shell script it is useful to imagine yourself
typing each statement in turn, for this is essentially what
the shell does in executing a shell script. You can execute a
shell script written in the C-shell scripting language by
submitting the script to a shell program as in:
% csh script
You can also add a directive as the first line of the script
indicating the shell program to use for execution.
% cat script
#!/bin/csh
...
To invoke this script without explicitly submitting it to a
shell you must use 'chmod' to make the script executable.
% chmod +x script
Now you should be able to execute the shell by simply typing
its name to the prompt.
% script
3. Conditional Statements
'if' - do something on the condition that ...
The syntax of conditional statements varies depending on the form of
the antecedent (the 'then' part of an conditional statement). If the
antecedent is a simple command and the condition and antecedent can
both fit on the same line, then you don't need to use the 'then'
keyword, indeed it will cause an error if you do include it.
if ( condition ) something
% set x = 0
% if ( $x == 0 ) echo "Big fat zero"
Big fat zero
% if ( ! ( $x > 0 ) && ! ( $x < 0 ) ) echo "Big fat zero"
Big fat zero
% set x = 1
% if ( $x != 0 ) echo "It ain't zero"
It ain't zero
% if ( ! ( $x == 0 ) ) echo "It ain't zero"
Ain't zero
% if ( ! $x ) echo "Big fat zero"
% set x = 0
% if ( ! $x ) echo "Big fat zero"
Big fat zero
In the last two conditionals, we're exploiting the fact that 0
is interpreted as false and hence '! 0' is interpreted as true.
For more complex antecedents (e.g., multiple commands) or statements
requiring additional conditions (e.g., an if-then-else statement) you
must use the 'then' keyword and it must appear exactly as shown below.
if ( condition ) then
something
something
endif
or
if ( condition ) then
something
something
else if ( condition ) then
something
something
endif
if ( $x > 0 ) then
echo "$x is greater than zero"
else if ( $x < 0 ) then
echo "$x is less than zero"
else if ( $x = 0 ) then
echo "$x is equal to zero"
endif
Note that using a variable bound to something other than
a number in a condition expecting a number is an error.
% set x = "not a number"
% if ( $x > 0 ) echo "Oops"
tcsh: if: Expression Syntax.
% set x = 1
% if ( $x > 0 ) echo "OK"
OK
4. Iteration Using Loops
'foreach' - do something for each item in a list of items
foreach variable ( list )
something
something
end
A script that sums the numbers appearing on the command line:
% echo sumnums
#!/bin/csh
set r = 0
foreach n ( $argv )
@ r += $n
end
echo $r
% sumnums 4 6 7 8
25
Note that 'n' in 'foreach n ( $argv )' isn't preceded by a '$' but
'argv' is preceded by a '$'. Think of the 'foreach' loop as setting
the variable 'n' to each item in the argument list. From this
perspective, 'foreach n ( $argv )' is analogous to 'set n = $argv[$i]'
for 'set i = 1' to 'set i = $#argv'.
'while' - do something as long as some condition is met
while ( condition )
something
something
end
The condition can be a flag set to 1 or 0, an inequality involving '<'
or '>', a string comparison such as '=~' or '!~' involving a pattern
on the right hand side, or a complex expression involving conjunction
('&&'), disjunction ('||'), and negation ('!'). Unless the loop is to
cycle indefinitely, there must be some aspect of the program that
changes so that eventually the exit condition is met (evaluates to
true) and the loop terminates. In the following example, the exit
condition '$i <= $n' is met when the variable 'i' which is incremented
as part of each iteration of the loop becomes greater than 'n'.
% cat sequence
#!/bin/csh
set n = $argv
set i = 1
set r = 0
while ( $i <= $n )
@ r += $i
@ i ++
end
echo $r
% sequence 5
45
Here's how you'd translate a 'foreach' loop into a 'while' loop.
set i = 1
while ( $i <= $#argv )
foreach n ( $argv ) @ n = $argv[$i]
... $n ... ... $n ...
... $n ... ... $n ...
end @ i ++
end
You can 'nest' loops; you can have 'foreach' loops inside of 'while'
loops or visa versa. The 'break' command allows you to terminate the
inner-most loop in which the 'break' appears. Here's a simple example
illustrating the use of the 'break' statement in checking a list of
numbers for primes:
% cat primes
#!/bin/csh
foreach n ( $argv )
@ d = $n / 2
while ( $d > 1 )
@ r = $n % $d
if ( $r == 0 ) break
@ d --
end
if ( $d == 1 ) echo $n
end
5. Command Substitution
The backtick or backquote enables you to assign a string or variable
the output of a command.
% echo "`date +%h` is the `date +%m`th month."
Sep is the 09th month.
When assigned to a variable the output of a command results in the
variable being assigned a list (or array) of words
% ls
09-15-04.txt 09-20-04.txt 09-22-04.txt 09-24-04.txt 09-27-04.txt
% set exercises = ( `ls` )
% echo $exercises
09-15-04.txt 09-20-04.txt 09-22-04.txt 09-24-04.txt 09-27-04.txt
As with any list you can determine how many items it contains, refer to
any particular item in the list by its index, or refer to a subsequence
of the items in a list.
% echo $#exercises
5
% echo $exercises[1]
09-15-04.txt
% echo $exercises[2]
09-20-04.txt
% echo $exercises[2-4]
09-20-04.txt 09-22-04.txt 09-24-04.txt
6. Advanced Topics
Using subroutines in shell scripts - advanced topic #1
You've already been using subroutines in shells; every time you invoke
a command inside of a script you're executing a subroutine. In some
cases, we execute a command for its 'side effects'. For example, you
might execute 'mkdir images' to create a directory. The creation of
the new directory is called a 'side effect'. In other cases, e.g.,
command substitution (above), we execute a command for the output
returned to the shell.
Unless you take pains to avoid it, the output of commands is directed
to the standard output and you see the output scrolling across your
screen. It doesn't matter whether you execute a command directly in
the shell or indirectly by executing a script which then executes
other commands:
% cat testout
#!/bin/csh
echo "Starting 'testout'"
subtest
echo "Finishing 'testout'"
% cat subtest
#!/bin/csh
echo "Executing 'subtest'"
% testout
Starting 'testout'
Executing 'subtest'
Finishing 'testout'
You can, of course, redirect output to files using '>' or '>>' and you
can pipe the output from one command into another command as in 'ls |
wc -l' (which counts the number of files and directories in the
current working directory). You can also use the backtick operator as
just mentioned to set a variable as in 'set var = `ls`' or specify the
list in a 'foreach' loop as in 'foreach var ( `ls ) ... end'. In the
following, we illustrate how use the output generated by one command
can determine the flow of control of another command by using
conditional statements. First we define a command that checks to see
if its only argument is prime.
% cat isprime
#!/bin/csh
set n = $argv
set p = 1
@ d = $n / 2
while ( $d > 1 && $p )
@ r = $n % $d
if ( $r == 0 ) set p = 0
@ d --
end
if ( $p ) then
echo 1
else
echo 0
endif
The echo commands are placed so that 'isprime' outputs a one (1) if its
sole argument is prime and a zero (0) otherwise. The 'isprime' command is
now a general utility that we can use in any number of contexts. We
can use the 'isprime' command in a conditional statement:
% if ( `isprime 3` ) echo "Prime"
Prime
% if ( ! `isprime 4` ) echo "Composite"
Composite
We can also use 'isprime' to write a more compact version of the
'primes' command:
% cat primes.sub
#!/bin/csh
foreach n ( $argv )
if ( `isprime $n` ) echo $n
end
Exiting from a shell command using 'exit' - advanced topic #2
The 'exit' command allows you to exit from a script at any point.
Getting user input in a shell script - advanced topic #3
The pseudo variable '$<' substitutes a line read from the standard
input, with no further interpretation. It can be used to read from
the keyboard in a shell script. See if you can figure out what the
following script does:
% cat guess
#!/bin/csh
set n = `date +"%M"`
while ( 1 )
set m = $<
if ( $m > $n ) then
echo "Too high"
else if ( $m < $n ) then
echo "Too low"
else
echo "You got it"
exit
endif
end
Using the exit status of a command - advanced topic #4
Every Unix command returns an exit status. By convention, if the
command is successful, it returns an exit status of zero. If the
command fails, then it returns an exit status greater than zero with
the exact value depending on how it failed; for example, grep returns
one (1) if it can't find the pattern it's looking for and two (2) if
it can't find the file supplied as its second argument.
In the C-shell the 'status' variable, is set to the exit status of
the last command executed:
% grep while isprime
while ( $d > 1 )
% echo $status
0
% grep foreach isprime
% echo $status
1
In writing a C-shell script you can use the keyword 'exit' with an
argument to make the script return a particular exit status based on
whatever you define as success or failure.
Here is another version of 'isprime' that returns an exit status of
zero (success) if its only argument is prime and one (failure)
otherwise.
% cat isprime
#!/bin/csh
set n = $argv
@ d = $n / 2
while ( $d > 1 )
@ r = $n % $d
if ( $r == 0 ) exit 1
@ d --
end
exit 0
% isprime 3
% echo $status
0
% isprime 4
% echo $status
1
There is a special syntax that allows you to use the exit status of
a command in an 'if' statement; you use curly braces instead of
the usual parentheses. The logic of using commands as conditional
is a little twisted however. Zero is false in a conditional while
one is true.
% if ( 0 ) echo "No"
% if ( ! 0 ) echo "No Way"
No Way
% if ( 1 ) echo "Way"
Way
In evaluating a command, if the command is successful (that is to say
it returns an exit status of zero), then the curly braces tell the
shell to return a one. If the command fails (it returns an exit
status greater than zero), then the curly braces tell the shell to
return a zero.
% if { isprime 3 } echo "Prime"
Prime
Now we can rewrite the 'primes' command as follows:
% cat primes
#!/bin/csh
foreach n ( $argv )
if { isprime $n } echo $n
end