foreach, switch, and one-based arrays
◈ 10 cardsWrite C shell branches and loops, build and slice a C shell array, and predict the two different errors a missing element produces depending on which spelling you used to ask for it.
Branching
The two-way branch keeps the shape you know and changes every keyword:
if ($#argv == 0) then
echo "usage: report file ..."
exit 1
endif
The condition goes in parentheses and is an expression, not a command — this is the deepest difference from the Bourne shell, where if runs a command and branches on its exit status. Because it is an expression, the C shell has its own operator set instead of test: == and != compare strings, > < >= <= compare integers, && and || combine, ! negates, and the file tests -d -e -f -r -w -x -z -o are written inside the parentheses directly. There is no -eq family at all, so the Bourne habit of writing -eq for numbers and = for strings has to be unlearned.
Chaining is else if (...) then, with a single endif closing the whole chain. There is no elif.
The pattern branch is switch:
switch ($choice)
case [dD]*:
date
breaksw
case [qQ]*:
exit 0
default:
echo "unknown choice"
endsw
The case labels are glob patterns, so [dD]* gives you case-insensitive matching for free. breaksw ends a case; leaving it out really does fall through to the next one, which is the opposite of the Bourne case, where omitting the terminator is a syntax error. default: is optional and conventionally last.
Looping
foreach walks a word list:
foreach file ($argv)
if (-d $file) echo "$file is a directory"
end
Note the one-line if with no then and no endif — legal only when the body is a single simple command, and worth knowing because it appears constantly in real scripts.
while (expr) ... end is the conditional loop. And that is the whole set. There is no until. The Bourne shell's "loop while this is false" has to be written by negating the condition yourself with !, and goto label exists for the cases where the structured forms run out. The C shell also has no functions, so a script that wants to reuse a block either repeats it, gotos to it, or becomes two script files.
Arrays, and why they are one-based
The C shell is the only shell in this course with real arrays, and they exist for a slightly accidental reason: set takes a list, so any parenthesised list is an array.
% set colours = (red green blue grey)
% echo $colours[2]
green
% echo $colours[2-3]
green blue
% echo $#colours
4
% echo $?colours
1
Four rules carry all the exam weight.
Indices start at 1. $colours[1] is red. There is no element zero — asking for one is an error, not an empty string. This is the opposite of Python, C and every other language in the degree, and it is the single most common slip.
Slices are inclusive at both ends. $colours[2-3] gives you elements two and three. Python's half-open convention does not apply.
The size is fixed at declaration. set colours[5] = white on a four-element array does not grow it; it reports set: Subscript out of range. To change the size you re-declare the whole array with a new list, usually by building the list first: set colours = ($colours white).
A slice is only an array if you wrap it in parentheses. set warm = ($colours[1-2]) makes a two-element array. set warm = $colours[1-2] does not — it assigns the joined words to a plain string variable, and $#warm will then tell you a story you did not expect. The parentheses are the array constructor, and they are needed on every assignment that is supposed to produce one.
One more, useful in practice: any command substitution assigned with set becomes an array of its output words. ` set files = ls ` gives you a per-filename-indexed array with no parsing at all.
The two failure modes of one missing argument
The script's arguments live in argv, so both spellings are available and they behave differently when the argument is not there:
% ./report # no arguments at all
% echo $9
# a blank line - silently null
% echo $argv[9]
argv: Subscript out of range.
The Bourne-compatible short form is quiet; the array form is loud. Neither is wrong, but a script that tests one and uses the other will fail in a way that is very hard to read. Guard on $#argv before touching any element and the question does not arise.