Appearance
Lecture 4
C-Shell Control Flow
- if
- then, else, endif
- switch
- case, default, breaksw, end
- while
- continue, break, end
- foreach
- continue, break, end
if statement
Simple format
sh
if ( <expression> ) <statement>
Full format
sh
if ( <expression> ) then
<statement>
else if ( <expression> ) then
<statement>
else
<statement>
endif
Spaces
C-shell is super picky about spaces between words and symbles. The spaces in the example above is necessary. If you get an error message while developing, first try fixing it by adding spaces
Csh condition test operators
Symbol | Name | Description |
---|---|---|
! | Negate | Works just as they do in C conditionals |
!= | Not equal | |
== | Equal | |
>, <, <=, >= | Relational | |
=~ | Match to wildcard pattern | [variable] =~ [pattern] |
!~ | Not match to wildcard pattern | [variable] !~ [pattern] |
Csh conditional file tests
sh
if ( -d filename ) # true if filename is a directory
if ( -e filename ) # true if filename exists
if ( -f filename ) # true if filename is a plain file
if ( -o filename ) # true if you own the file
if ( -r filename ) # true if filename is readable
if ( -w filename ) # true if filename is writable
if ( -x filename ) # true if filename is executalbe
if ( -z filename ) # true if filename is empty
Examing Flags From Arguments
Suppose you want to write a script that accepts a -r
option as an script arguments. The following won't work.
sh
if ( $1 == -r ) echo "The -r flag was given"
If $1
is indeed -r
, after the substitution, it would become
sh
% if ( -r == -r ) echo "The -r flag was given"
if: Missing file name.
which examine if the file ==
is readable and a trailing -r
causing the error.
Adding a dummy prefix
The best you can do is to add a dummy prefix, for example
sh
if ( d$1 == d-r ) echo "The -r flag was given"
while loop
Emulating for loop with while
sh
#!/bin/tcsh
@ i = 0
while ( `expr $i \< 3` )
echo -n $i
@ i++
end
Output
sh
0 1 2
foreach
Dive into an example
sh
#!/bin/tcsh
foreach person (Bob Susan Nico)
echo Hello $person
end
Output
sh
Hello Bob
Hello Susan
Hello Nico
Still A Better For Loop
sh
#!/bin/tcsh
foreach i (`seq 10 10 100`)
echo $i + 1 = `expr $i + 1`
end
Output
sh
10 + 1 = 11
20 + 1 = 21
30 + 1 = 31
40 + 1 = 41
50 + 1 = 51
60 + 1 = 61
70 + 1 = 71
80 + 1 = 81
90 + 1 = 91
100 + 1 = 101
A Delete Script
Let's write a scrtpt!
List of functionalities
For each command-line argument
- Ask about whether it should be deleted.
- Read the user's response from stdin.
- Perform the action indicated by the user's response.
Source code
sh
#!/bin/tcsh
foreach name ($argv) # loop through arguments
if ( -f $name ) then # test if it is a file
echo -n "delete the file $name (y/n/q)? "
else
echo -n "delete the entire directory"\
"$name (y/n/q)? "
endif
set ans = $< # This symbol indicates to take input from keyboard
switch ( $ans )
case n:
continue
case q:
exit # no args means $? will be 0
case y:
rm -rf $name
endsw
end
Quote
So far, we've seen many special symbols that have their own special meanings, such as |
is for pipelining, $
is for variables, etc.. However, what if we want those symbols literally, i.e. the plaintext, no special meaning. Then we quote them.
There're three quoting symbols
Symbol | Description |
---|---|
\ | Single charater quote |
' | Strong quotes |
" | Weak quotes |
Strong quote
Stong quote '
. What do you mean by strong? If it's strong, it can turn other special symbols into plaintexts. If a special symbol is stronger then it, such as ![prefix]
, then it cannot suppress the symbol
sh
% echo 'Hi! Hi!'
Hi! Hi!
% echo 'Hi!Hi!'
Hi!: Event not found.
On the line 4, the !
thought Hi!
was a prefix of a previous command, but obviously, we don't have commnads with prefix of Hi!
.
Weak quote
Weak quote "
. Allow dollar sign $
and backtick execution `[command]`
to expand inside two quotes. i.e. it's not strong enough to supress these two special symbols.
sh
% echo "Is the executable under $PATH?"
Is the executable under /usr/local/opt/tcl-tk/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/Library/TeX/texbin:/Library/Apple/usr/bin?
% echo "My current directory is `pwd`"
My current directory is /Users/ernestchu
Strength
\
> '
> "
\
supresses!
(history expansion) and all other commands, even EOL can be quoted. That's why we can add\
at the end of lines to have multi-line commands.'
as weaker quotes, cannot supresses!
"
as even weaker quotes, cannot surpress!
,$
and backticks``
Quotes Within Quotes
The strength is based on what commands do they surpress. But it doesn't mean \
surpresses '
or '
surpresses "
.
sh
% echo "'"
'
% echo '"'
"
% echo '\'"\"
\\
% echo \'\\\"
'\"
% echo '''
Unmatched '''.
% echo """
Unmatched """.
Interestingly, the only quote that can quote itself is \
. Neither '
nor "
can quote themselves. Why? We'll see below
The shell parsed commands from left to right
sh
'I am in the stong quote interpretation rule.'
↑ ↑
│ turn off ─┘
└─ turn on strong quote interpretation
So actually, quotes doesn't generate string, they just change the way a shell interpretates commands.
Once it entered the zone by hitting '
, the shell turn on plaintext interpretation until it hits next '
. (Yet except !
)
Exercise
% echo 'This doesn\'t work'
Unmatched '''.
% echo 'Turn off the "strong quote interpretation" first, then it'\''ll work!'
Turn off the "strong quote interpretation" first, then it'll work!
Backslash'es'
How does echo
interpret the \
symbol?
Can we use the escape sequences inside quotes?
It's really depends on the shell
sh
# In tcsh on Steve's computer
% echo 'a\tb\\'
a b\
# In tcsh on my MacBook
% echo 'a\tb\\'
a\tb\\
We'd better follow Steve's computer...
From 1 to n backslashes
sh
% echo \ # 1
?
# this is not an output, it's an indicator telling you
# that the command hasn't finished, because EOL was suprressed
% echo \\ # 2
\
% echo \\\ # 3
? # same as line 2
% echo \\\\ # 4
\
# csh substitute \\\\ with \\. However, when \\ is sent into echo
# as an argument, echo has it's own substitution again, thus /
% echo \\\\\ # 5
? # same as line 2, so we know odd number of \ would all get this
% echo \\\\\\\\ # 8
\\ # echo receives \\\\, hence substitute with \\ internally
% echo \\\\\\\\\\\\\\\\ # 16
\\\\ # echo receives \\\\\\\\.
% echo \\\\\\\\\\\\\\\\ | xargs echo
\\ # been through two internal substitutions of echo
% echo '\\\\\\\\' | xargs echo
\\ # quoted, hence \\\\\\\\ -> \\\\ -> \\
xargs Do Not Substitute
We've said echo
does it own expansion, what about xargs
? Does the \\
in xargs echo \\
become \
before send into echo
? The anwser is negative. Let's look at an example on wildcard expansion
sh
% ls ?
A B
% echo ? '?'
A B ?
% echo ? | xargs echo
A B # A B were sent to xargs echo, so it makes sense
% echo '?' | xargs echo
? # ? was sent to xargs echo, and xargs don't do the expansion
% echo `echo '?'`
A B
# while backticks simply substitute echo '?' with ? (without quotes).
# so it becomes echo ?
Debugging
Not sure if you quote correctly? Especially when you writing a script. Well, Unix provides a great tool to help you out!
set echo
print every command executed after the subsitutionset verbose
print every command executed before the substution
And you can unset
them if you want to turn off the behavior.