[comp.unix.wizards] csh problem involving nested ifs?

marty@wuphys.UUCP (Marty Olevitch) (07/10/89)

It appears that csh does not correctly handle nested if-then-else
statements. The following script illustrates the problem, which appears
in versions of csh on MORE/bsd (mt Xinu 4.3), SunOS 3.1, SunOS 4.0, and
Ultrix 2.0. Am I missing some bug in this script? If not, it looks like
a bug in csh (an equivalent sh program works fine).

#! /bin/csh
set v1 = $1
set v2 = $2
echo "v1 = $v1 and v2 = $v2"
if($v1 == 1) then
	echo checkpoint 1
	if($v2 == 1) then
		echo 11
	else 
		echo 12
	endif
	echo checkpoint 2
else 
	echo checkpoint 3
	if($v2 == 1) then
		echo 21
	else 
		echo 22
	endif
	echo checkpoint 4
endif

Here is a little chart showing the command lines issued, the expected
results, and the actual results.

command line	expect			get
------------	------			-------
iftest 1 1	checkpoint 1		checkpoint 1
		11			11
		checkpoint 2		checkpoint 2
					checkpoint 4
					
iftest 1 2	checkpoint 1		checkpoint 1
		12			12
		checkpoint 2		checkpoint 2
					checkpoint 4
						
iftest 2 1	checkpoint 3		12
		21			checkpoint 2
		checkpoint 4		checkpoint 4

iftest 2 2	checkpoint 3		12
		22			checkpoint 2
		checkpoint 4		checkpoint 4

Marty Olevitch				Internet: marty@wuphys.wustl.edu
Washington University			UUCP:     uunet!wugate!wuphys!marty
Physics Department, Campus Box 1105	Bitnet:   marty@wuphys.WUSTL
St Louis MO 63130 USA			Tel:      (314) 889-6285

tale@pawl.rpi.edu (David C Lawrence) (07/11/89)

In <685@wuphys.UUCP> marty@wuphys.UUCP (Marty Olevitch) writes:
Marty> It appears that csh does not correctly handle nested if-then-else
Marty> statements. The following script illustrates the problem, which appears
Marty> in versions of csh on MORE/bsd (mt Xinu 4.3), SunOS 3.1, SunOS 4.0, and
Marty> Ultrix 2.0. Am I missing some bug in this script? If not, it looks like
Marty> a bug in csh (an equivalent sh program works fine).

Yes it's a bug, no it's not a bug.  It's one of those ambivalent
things.  If you know what it is, you adjust your style by one
character or so and life goes on correctly again.

Major hacking on the script follows ...
Marty> #! /bin/csh
[...]
Marty> if($v1 == 1) then
Marty> 	echo checkpoint 1
Marty> 	if($v2 == 1) then
[...]
Marty> 	if($v2 == 1) then
[...]

If you change the "if(" to "if (" in each of the nested conditionals
you should find that your problem has vanished.  The problem is that
whitespace becomes significant within the first if block, at least as
far as further nested if blocks are concerned.  The parser used within
the if block is not the same one which parsed out the top if.

Dave
--
 (setq mail '("tale@pawl.rpi.edu" "tale@itsgw.rpi.edu" "tale@rpitsmts.bitnet"))
        "Drinking coffee for instant relaxation?  That's like drinking
               alcohol for instant motor skills."  -- Marc Price

chris@mimsy.UUCP (Chris Torek) (07/11/89)

In article <685@wuphys.UUCP> marty@wuphys.UUCP (Marty Olevitch) writes:
>It appears that csh does not correctly handle nested if-then-else
>statements.

This is false.  It does handle them, but it is finicky.

>Am I missing some bug in this script?

[all but the `if' lines deleted]

>if($v1 == 1) then
>	if($v2 == 1) then
>	if($v2 == 1) then

You must put a space after the `if', before the `('; and you must have
a space between the `)' and the `then' (which must be the last word on
the line, after stripping comments).

The reason is that the C shell's `skip to end of false construct' code
uses a completely different line parser than usual.  The `execute this line'
parser breaks lines at whitespace and at the characters ( ) & | and
perhaps a few others.  (&& and || are collected as a unit before causing
word breaks.)  The `skip to end of false if' routine winds up doing

	if (strcmp("if(", "if") == 0 && strcmp("then", "then") == 0)

in the particular cases given above.  Had you written

	if ($v2 == 1)then

it would have tried

	if (strcmp("if", "if") == 0 && strcmp(")then", "then") == 0)

and also failed.  Only

	if ($v2 == 1) then

and similar constructs pass its test.  (One such construct is

	if (this, {which is not &syntactically! correct, goes undetected then
		the c shell sucks
	endif

which counts as an `if-then' during false-if skipping, but causes an
error otherwise.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

envbvs@epb2.lbl.gov (Brian V. Smith) (07/11/89)

In article <685@wuphys.UUCP>, marty@wuphys.UUCP (Marty Olevitch) writes:
< 
< It appears that csh does not correctly handle nested if-then-else
< statements. The following script illustrates the problem, which appears
< in versions of csh on MORE/bsd (mt Xinu 4.3), SunOS 3.1, SunOS 4.0, and
< Ultrix 2.0. Am I missing some bug in this script? If not, it looks like
< a bug in csh (an equivalent sh program works fine).
< 

[ csh script follows ]

Well, on our Vaxstation II running Ultrix 2.0 machine and out
Vaxstation 2000's and II's running Ultrix 3.0, the script worked
just as it should (the "expected" results).

_____________________________________
Brian V. Smith    (bvsmith@lbl.gov)
Lawrence Berkeley Laboratory
We don't need no signatures!

envbvs@epb2.lbl.gov (Brian V. Smith) (07/11/89)

In article <TALE.89Jul10131139@imagine.pawl.rpi.edu>, tale@pawl.rpi.edu (David C Lawrence) writes:
< Yes it's a bug, no it's not a bug.  It's one of those ambivalent
< things.  If you know what it is, you adjust your style by one
< character or so and life goes on correctly again.
< 
< Major hacking on the script follows ...
< Marty> #! /bin/csh
< [...]
< Marty> if($v1 == 1) then
< Marty> 	echo checkpoint 1
< Marty> 	if($v2 == 1) then
< [...]
< Marty> 	if($v2 == 1) then
< [...]
< 
< If you change the "if(" to "if (" in each of the nested conditionals
< you should find that your problem has vanished.  The problem is that
< whitespace becomes significant within the first if block, at least as
< far as further nested if blocks are concerned.  The parser used within
< the if block is not the same one which parsed out the top if.

I *didn't* include the spaces as David recommends, and I still didn't see
the problem under Ultrix 2.0 OR Ultrix 3.0. The script (and, hence csh)
works fine.

I used "cut/paste" with X to copy the script, so I know I didn't make any typos.
_____________________________________

Brian V. Smith    (bvsmith@lbl.gov)
Lawrence Berkeley Laboratory
We don't need no signatures!

peter@ficc.uu.net (Peter da Silva) (07/11/89)

Basically, it is undesirable to use the 'C' shell for any complicated
scripts... or any scripts at all other than .cshrc and .login.
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Business: peter@ficc.uu.net, +1 713 274 5180. | Th-th-th-that's all folks...
Personal: peter@sugar.hackercorp.com.   `-_-' |  -- Mel Blanc
Quote: Have you hugged your wolf today?  'U`  |     May 30 1908 - Jul 10 1989

grr@cbmvax.UUCP (George Robbins) (07/13/89)

In article <4948@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
> Basically, it is undesirable to use the 'C' shell for any complicated
> scripts... or any scripts at all other than .cshrc and .login.

This is a fairly widely held viewpoint, but when you get down to it,
neither the bourne shell, nor c-shell provide very elegant programming
languages.  C-shell does allow "prettier code", but the inconsistancies
will nip you from time to time.  Bourne shell scripts do have the
major advantage of being transportable back into the USG world, which
may or may not matter for the task at hand.

I here there's now a alt.computers.religion topic for just this sort of rwar...

-- 
George Robbins - now working for,	uucp: {uunet|pyramid|rutgers}!cbmvax!grr
but no way officially representing	arpa: cbmvax!grr@uunet.uu.net
Commodore, Engineering Department	fone: 215-431-9255 (only by moonlite)

rbj@dsys.ncsl.nist.gov (Root Boy Jim) (07/14/89)

? From: Peter da Silva <peter@ficc.uu.net>

? Basically, it is undesirable to use the 'C' shell for any complicated
? scripts... or any scripts at all other than .cshrc and .login.

I disagree. CSH is much more natural to use than SH. The constructs are
more natural. In csh, I don't have to resort to `expr' and `test'
because these are builtin. The only things I see SH is good for are:

	1) portability - sh exists everywhere
	2) trivial scripts - starts faster
	3) complex file redirection - but how often do you do 3> anyway?
	4) shell fns - neat
	5) piping to `for' and `while' - but how often?

Of course, newer SH's and KSH help quite a bit.

? Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
? Business: peter@ficc.uu.net, +1 713 274 5180. | Th-th-th-that's all folks...
? Personal: peter@sugar.hackercorp.com.   `-_-' |  -- Mel Blanc
? Quote: Have you hugged your wolf today?  'U`  |     May 30 1908 - Jul 10 1989

	Root Boy Jim
	Have GNU, Will Travel.

m5@lynx.uucp (Mike McNally) (07/14/89)

rbj@dsys.ncsl.nist.gov (Root Boy Jim) writes:

>I disagree. CSH is much more natural to use than SH. The constructs are
>more natural. 

Like the nightmare of trying to continue quoted strings across lines?
(OK, OK, a backslash or two isn't that bad).  C shell variable manipulation
always confuses me too.  Then again, the quaint Bourne shell technique of 
using "set $blah" to implement arrays is a bit crude.  The bizarre csh
problems with nested complex commands are of course a true pain (like
the way quotes magically disappear after the first iteration of a loop...).

>                  In csh, I don't have to resort to `expr' and `test'
>because these are builtin. 

Agreed, although getting at test via [ ... ] isn't painful.

>                         The only things I see SH is good for are:
>	1) portability - sh exists everywhere
>	2) trivial scripts - starts faster
Like *lots* faster.  32K!!!!
>	3) complex file redirection - but how often do you do 3> anyway?
Never.
>	4) shell fns - neat
I guess.
>	5) piping to `for' and `while' - but how often?
**** a l l   t h e   t i m e !!!!!!!!!!!!!  This is in my opinion
one of the nicest things about the Bourne shell (or maybe it's one
of the worst things about the C shell).

>Of course, newer SH's and KSH help quite a bit.
Give me version 7 sh or give me death!

>	Root Boy Jim
>	Have GNU, Will Travel.

Why am I posting this?  Maybe somebody should yell at me for wasting
bandwidth.

-- 
Mike McNally                                    Lynx Real-Time Systems
uucp: {voder,athsys}!lynx!m5                    phone: 408 370 2233

            Where equal mind and contest equal, go.

hjg@amms4.UUCP (Harry Gross) (07/14/89)

In article <7293@cbmvax.UUCP> grr@cbmvax.UUCP (George Robbins) writes:

>I here there's now a alt.computers.religion topic for just this sort of rwar...
		      ^^^^^^^^^^^^^^^^^^^^^^

Read that as alt.religion.computers - it was created about a week or so ago, and
I already have 141 articles posted to it.

-- 
		Harry Gross				 |  reserved for
							 |  something really
Internet: hjg@amms4.UUCP   (we're working on registering)|  clever - any
UUCP: {jyacc, rna, bklyncis}!amms4!hjg			 |  suggestions?

peter@ficc.uu.net (Peter da Silva) (07/15/89)

Might I make a request. Would people who post news *AND* send mail please
*SAY SO*?

Anyway, I bitched about how crummy CSH is for scripts...

In article <20243@adm.BRL.MIL>, rbj@dsys.ncsl.nist.gov (Root Boy Jim) writes:
> I disagree. CSH is much more natural to use than SH. The constructs are
> more natural.

The constructs are more conventional, but they sure don't match UNIX
filter semantics very well. What do you mean by "natural"?

> In csh, I don't have to resort to `expr' and `test'
> because these are builtin.

In SH I don't have to worry about my control structures screwing up,
because they're parsed instead of being implemented as commands. CSH
control structures remind me more of assembler macros.

> The only things I see SH is good for are:

> 	1) portability - sh exists everywhere
> 	2) trivial scripts - starts faster
> 	3) complex file redirection - but how often do you do 3> anyway?

Not often, but I do 2> quite a bit.

> 	4) shell fns - neat
> 	5) piping to `for' and `while' - but how often?

(piping, redirecting, etc...)

All the time. This construct:

generate_a_list_of_file_names |
  while read FNAME
  do
    do_something_with FNAME
  done

is second nature. And it's impossible in csh without massive inconvenience.
By comparison, having TEST and EXPR builtin is a minor optimisation.

> Of course, newer SH's and KSH help quite a bit.

For interactive use, maybe. I still prefer CSH history.
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Business: peter@ficc.uu.net, +1 713 274 5180. | Th-th-th-that's all folks...
Personal: peter@sugar.hackercorp.com.   `-_-' |  -- Mel Blanc
Quote: Have you hugged your wolf today?  'U`  |     May 30 1908 - Jul 10 1989

tchrist@convex.UUCP| (Tom Christiansen) (07/15/89)

In article <7293@cbmvax.UUCP| grr@cbmvax.UUCP (George Robbins) writes:
|In article <4948@ficc.uu.net| peter@ficc.uu.net (Peter da Silva) writes:
|| Basically, it is undesirable to use the 'C' shell for any complicated
|| scripts... or any scripts at all other than .cshrc and .login.

sigh.  i used to disbelieve this.  i've learned the hard way.  it's
more true that not true.

|This is a fairly widely held viewpoint, but when you get down to it,
|neither the bourne shell, nor c-shell provide very elegant programming
|languages.  C-shell does allow "prettier code", but the inconsistancies [sic]
|will nip you from time to time.  Bourne shell scripts do have the
|major advantage of being transportable back into the USG world, which
|may or may not matter for the task at hand.

This is also so.   That's why where I used to write shell scripts with
reams of embedded awk and sed calls, I now use perl, another wonderful
piece of Larry Wall-ware.  It's a real programming language, optimized
for sysadmin tasks.  If you haven't looked at it, consider doing so.  
And it runs on a googol of architectures, thanks to a lot of work 
towards this goal and his metaconfig program.  Try it, you'll like it.


--tom

srg@quick.COM (Spencer Garrett) (07/16/89)

In article <5016@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:

-> generate_a_list_of_file_names |
->   while read FNAME
->   do
->     do_something_with FNAME
->   done
-> 
-> is second nature. And it's impossible in csh without massive inconvenience.
-> By comparison, having TEST and EXPR builtin is a minor optimisation.

What are you talking about?  It's easy and much more natural under csh.

foreach fname (`generate_a_list_of_file_names`)
	do_something_with_fname
end

peter@ficc.uu.net (Peter da Silva) (07/16/89)

In article <3171@quick.COM>, srg@quick.COM (Spencer Garrett) writes:
> In article <5016@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:
> -> generate_a_list_of_file_names |
> ->   while read FNAME
> ->   do
> ->     do_something_with FNAME
> ->   done

> -> is second nature. And it's impossible in csh without massive inconvenience.

> What are you talking about?  It's easy and much more natural under csh.

> foreach fname (`generate_a_list_of_file_names`)
> 	do_something_with_fname
> end

Oh, right. I can do:

	for i in `generate a list of file names`
	do
		...
	done

too. But what if thise file names are generated by:

	find / -type f -print

Just how much stuff can *you* put in command substitutions?

To do this under csh, I basically have to do something like:

cat > foo << 'EOF'
#
do_something_with_$*
EOF
find ... | xargs csh foo
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Business: peter@ficc.uu.net, +1 713 274 5180. | Th-th-th-that's all folks...
Personal: peter@sugar.hackercorp.com.   `-_-' |  -- Mel Blanc
Quote: Have you hugged your wolf today?  'U`  |     May 30 1908 - Jul 10 1989

merlyn@iwarp.intel.com (Randal Schwartz) (07/17/89)

In article <3171@quick.COM>, srg@quick (Spencer Garrett) writes:
| In article <5016@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:
| 
| -> generate_a_list_of_file_names |
| ->   while read FNAME
| ->   do
| ->     do_something_with FNAME
| ->   done
| -> 
| -> is second nature. And it's impossible in csh without massive inconvenience.
| -> By comparison, having TEST and EXPR builtin is a minor optimisation.
| 
| What are you talking about?  It's easy and much more natural under csh.
| 
| foreach fname (`generate_a_list_of_file_names`)
| 	do_something_with_fname
| end

Unless (and there are far far far too many "unless"-es with CSH...)

generate_list_of_file_names returns more than 5120 characters, *or*
the resulting list can't be globbed properly (I just got bit on
/bin/*... try it... it bites the big one on "/bin/[") *or* some other
combination of events that I'll probably run across tomorrow...
Gaaaaccckkk!

Gimme /bin/sh any day.  True parsing.  No nonsense.

Of course, as soon as /bin/ksh becomes *the* standard, I'll like it
much better.  Until then, I'm stuck with csh for my top level shell
because of command history and job control, but all my scripts (well,
non-Perl scripts :-) are /bin/sh.

Just an old-time UNIX hacker,
-- 
/== Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ====\
| on contract to Intel, Hillsboro, Oregon, USA                           |
| merlyn@iwarp.intel.com ...!uunet!iwarp.intel.com!merlyn	         |
\== Cute Quote: "Welcome to Oregon... Home of the California Raisins!" ==/

bet@orion.mc.duke.edu (Bennett Todd) (07/18/89)

In article <3171@quick.COM>, srg@quick (Spencer Garrett) writes:
>In article <5016@ficc.uu.net>, peter@ficc.uu.net (Peter da Silva) writes:
>
>-> generate_a_list_of_file_names |
>->   while read FNAME
>->   do
>->     do_something_with FNAME
>->   done
>-> 
>-> is second nature. And it's impossible in csh without massive inconvenience.
>-> By comparison, having TEST and EXPR builtin is a minor optimisation.
>
>What are you talking about?  It's easy and much more natural under csh.
>
>foreach fname (`generate_a_list_of_file_names`)
>	do_something_with_fname
>end

I am *so* glad bash is out; I used to use a C-shell for interactive work
just to get history and job control; I was always having to fire up
/bin/sh to do serious looping work. Here are some things csh croaks on:

	find ... -print | while read filename;do
		...
	done

If you try rewrite that as

	foreach f (`find ... -print`)
		...
	end

csh will bomb on command line too long.

	IFS=:
	while read login passwd uid gid gcos home shell;do
		# process /etc/passwd with fields broken down
	done </etc/passwd

I wouldn't know how to try that in csh -- without running a separate
invocation of sed(1) over /etc/passwd once per record.

	for f in ... # or while ...
		...
	done &

csh won't let me background an entire loop.

Basically, I get the impression that csh's attempts at programming
constructs are afterthoughts hacked in imperfectly, rather than properly
parsed grammar constructs.

-Bennett
bet@orion.mc.duke.edu