From: bazyar@teal.csn.org (Jawaid Bazyar)
Newsgroups: comp.unix.questions
Subject: The Revenge of CSH
Date: 28 Dec 92 15:31:18 GMT
Organization: Colorado SuperNet, Inc.


  The reason for this reply is that I'm one of a number of folks working
on a csh clone (called GSH, and for the GNO/ME system for the Apple IIgs).
We intend to fix most if not all of these problems as we implement csh
from scratch on a 16-bit platform.  But I take offense to the manner in 
which csh's bugs are listed, and then be stated "csh sucks, is 
totally unworthy", basically.  
  So, let the flamefest begin.

> From: Tom Christiansen <tchrist@convex.COM>
> Subject: Csh Programming Considered Harmful 
> Originator: tchrist@pixel.convex.com
> X-Disclaimer: This message was written by a user at CONVEX Computer
>               Corp. The opinions expressed are those of the user and
>               not necessarily those of CONVEX.
 
> The following periodic article answers in excruciating detail
> the frequently asked question "Why shouldn't I program in csh?".
> It is available for anon FTP from convex.com in /pub/csh.whynot .
> 
> 		   Csh Programming Considered Harmful
> 
> Resolved: The csh is a tool utterly inadequate for programming, and
> its use for such purposes should be strictly banned.

   Oops.  I've used csh scripts before.  Shall I beg forgiveness?
 
> I am continually shocked and dismayed to see people write test cases,
> install scripts, and other random hackery using the csh.  

  I am continually shocked and dismayed to see people not have
command line editing, arrow-key history, job control, or many
of the other things that csh (tcsh) provides and /bin/sh does not.

> Lack of
> proficiency in the Bourne shell has been known to cause errors in /etc/rc
> and .cronrc files, which is a problem, because you *must* write these files
> in that language.

  That would seem to be braindamage on the part of the kernel and cron,
don't you think?

[..]
 
> FILE DESCRIPTORS
> 
> The most common problem encountered in csh programming is that
> you can't do file-descriptor manipulation.  All you are able to 
> do is redirect stdin, or stdout, or dup stderr into stdout. 
> Bourne-compatible shells offer you an abundance of more exotic
> possibilities.    

  Possibilities that could be added to csh, if anyone cared to.
[...]

> Writing Files
> 
> In the Bourne shell, you can open or dup random file descriptors.
> For example, 
>     exec 2>errs.out
> means that from then on, stderr goes into errs file.
> Or what if you just want to throw away stderr and leave stdout
> alone?    Pretty simple operation, eh?
>     cmd 2>/dev/null
> Works in the Bourne shell.  In the csh, you can only make a pitiful 
> attempt like this:
>     (cmd > /dev/tty) >& /dev/null
> But who said that stdout was my tty?  So it's wrong.  This simple
> operation *CANNOT BE DONE* in the csh.

  Trivially fixed, by making >& _only_ redirect stderr, and having
the shell check appropriately for > and >& going to the same file.
I envison, oh, 10 lines of code? 

[...]
 
> Reading Files
> 
> In the csh, all you've got is $<, which reads a line from your tty.  What
> if you've redirected stdin?  Tough noogies, you still get your tty, which 
> you really can't redirect.  Now, the read statement 
> in the Bourne shell allows you to read from stdin, which catches
> redirection.  It also means that you can do things like this:

  Okay, add a read statement to csh.
 
> Although in older ones where read only goes from 0, you trick it:

  [..]

> You want to know the status of C, well, that's easy: it's in $?, or
> $status in csh.  But if you want it from A, you're out of luck -- if
> you're in the csh, that is.  In the Bourne shell, you can get it, although
> doing so is a bit tricky.  Here's something I had to do where I ran dd's

   "bit tricky" seems a lot like "totally unsuited to" a task, to me.
  
> stderr into a grep -v pipe to get rid of the records in/out noise, but had
> to return the dd's exit status, not the grep's:
> 
>     device=/dev/rmt8
>     dd_noise='^[0-9]+\+[0-9]+ records (in|out)$'
>     exec 3>&1
>     status=`((dd if=$device ibs=64k 2>&1 1>&3 3>&- 4>&-; echo $? >&4) |
> 		egrep -v "$dd_noise" 1>&2 3>&- 4>&-) 4>&1`
>     exit $status;

  Hmm.  Too bad that's unreadable! :)
 
> COMMAND ORTHOGONALITY
> 
> Built-ins
> 
> The csh is a horrid botch with its built-ins.  You can't put them
> together in many reasonable way.   Even simple little things like this:	
> 
> 	    % time | echo

  He admits that the code is nonsensical!

> which while nonsensical, shouldn't give me this message:
> 
> 	    Reset tty pgrp from 9341 to 26678

  I.e., csh has a bug.  Not a design deficiency, but a bug.

> Others are more fun:
> 
> 	    % sleep 1 | while
> 	    while: Too few arguments.
> 	    [5] 9402
> 	    % jobs
> 	    [5]     9402 Done                 sleep |
> Some can even hang your shell.  Try typing ^Z while you're sourcing 
> something, or redirecting a source command.  Just make sure you have
> another window handy.  Or try 
> 
>     % history | more
> 
> on some systems.

  "on some systems" means that there's a bug in csh on some systems,
not that csh is devil-spawn.
 
> Flow control
> 
> You can't mix flow-control and commands, like this:
>     
>     who | while read line; do
> 	echo "gotta $line"
>     done
> 
> You can't combine multiline constructs in a csh using semicolons.
> There's no easy way to do this
> 
>     alias cmd 'if (foo) then bar; else snark; endif'

  This is an implementation problem; csh's designers decided to do it
one way, and that's what they got.  Strangely enough, it's _less_
code to make it as free-form as possible, and in fact GSH 2.0 will
allow the above.

> Stupid parsing bugs

  ^^^^  He devotes a whole section to implementation errors.  These
are _bugs_, i.e. they could be fixed or avoided in a new implementation
(like GSH).

> SIGNALS
> 
> In the csh, all you can do with signals is trap SIGINT.  In the Bourne
> shell, you can trap any signal, or the end-of-program exit.    For example,
> to blow away a tempfile on any of a variety of signals:

  Too bad the Bourne shell doesn't support real BSD job control.
 
> 6.  QUOTING
 
> You can't quote things reasonably in the csh:
 
>     set foo = "Bill asked, \"How's tricks?\""
 
> doesn't work.  This makes it really hard to construct strings with
> mixed quotes in them.  In the Bourne shell, this works just fine. 
> In fact, so does this:
> 
>      cd /mnt; /usr/ucb/finger -m -s `ls \`u\``
> 
> Dollar signs cannot be escaped in double quotes in the csh.  Ug.
> 
>     set foo = "this is a \$dollar quoted and this is $HOME not quoted" 
>     dollar: Undefined variable.

  Another bug.
 
> You have to use backslashes for newlines, and it's just darn hard to
> get them into strings sometimes.

  Design limitation (gosh, finally got one).
 
> Say what?  You don't have these problems in the Bourne shell, where it's
> just fine to write things like this:
>     echo 	'This is 
> 	     some text that contains
> 	     several newlines.'
 
  So what?
 
> As distributed, quoting history references is a challenge.  Consider:
> 
>     % mail adec23!alberta!pixel.Convex.COM!tchrist
>     alberta!pixel.Convex.COM!tchri: Event not found.

  Gosh, that's like saying "csh is broken because I can't do this:

  mail FX3$LRG@a.b.c.d
 
> VARIABLE SYNTAX
> 
> There's this big difference between global (environment) and local
> (shell) variables.  In csh, you use a totally different syntax 
> to set one from the other.  
> 
> In Bourne shell, this 
>     VAR=foo cmds args
>  is the same as
>     (export VAR; VAR=foo; cmd args)
> or csh's
>     (setenv VAR;  cmd args)
>  
> You can't use :t, :h, etc on envariables.  Watch:
> 	echo Try testing with $SHELL:t
> 
> It's really nice to be able to say
>     
>     ${PAGER-more}
> or
>     FOO=${BAR:-${BAZ}}
> 
> to be able to run the user's PAGER if set, and more otherwise.
> You can't do this in the csh.  It takes more verbiage.

  Excuse me?  "You can't do this" and "It takes more verbiage" means
IT CAN BE DONE.  
  Why didn't you add: "C can't pass strings around on the stack like Pascal can.
It takes more verbiage.  C is obviously inferior to Pascal."
 
> You can't get the process number of the last background command from the
> csh, something you might like to do if you're starting up several jobs in
> the background.  In the Bourne shell, the pid of the last command put in
> the background is available in $!.

  And it could be added to csh with about two lines of code.

> EXPRESSION EVALUATION
> 
> Consider this statement in the csh:
> 
> 
>     if ($?MANPAGER) setenv PAGER $MANPAGER
> 
> 
> Despite your attempts to only set PAGER when you want
> to, the csh aborts:
> 
>     MANPAGER: Undefined variable.
> 
> That's because it parses the whole line anyway AND EVALUATES IT!
> You have to write this:
> 
>     if ($?MANPAGER) then
> 	setenv PAGER $MANPAGER
>     endif
> 
> That's the same problem you have here:
> 
>     if ($?X && $X == 'foo') echo ok
>     X: Undefined variable
> 
> This forces you to write a couple nested if statements.  This is highly
> undesirable because it renders short-circuit booleans useless in
> situations like these.  If the csh were the really C-like, you would
> expect to be able to safely employ this kind of logic. 

   A valid point (finally!).  But, it could still be fixed pretty easily.

> While the csh does have built-in expression handling, it's not
> what you might think.  In fact, it's space sensitive.  This is an
> error
> 
>    @ a = 4/2
> but this is ok
>    @ a = 4 / 2

  So golly, it's got a stupid lexer.  Want my replacement?  And is
it really that big a deal to delimit the numbers with spaces? I mean,
there are plenty of other situations you must use spaces.  I'll bet
not even Bourne shell knows what to do with:

  ls-1/bin/foo

  According to you, there exists the possiblity that a lexer smart
enough to deal with that statement exists, therefore csh is inherently
flawed because it doesn't implement that lexer?
 
> RANDOM BUGS

  They're bugs, so they could be fixed.
 
> SUMMARY
> While some vendors have fixed some of the csh's bugs (the tcsh also does
> much better here), many have added new ones.  Most of its problems can
> never be solved because they're not actually bugs per se, but rather the
> direct consequences of braindead design decisions.  It's inherently flawed.

  That's garbage.  You have shown no proof for that statement.  I'm
supposed to read a bug list and then believe that all the bugs are
caused because csh is "inherently flawed"?
 
> Do yourself a favor, and if you *have* to write a shell script, do it in the 
> Bourne shell.  It's on every UNIX system out there.  However, behavior 
> can vary.

  Gee.. that means that /bin/sh isn't the universal be-all end-all of
shells?  You don't say?  You mean /bin/sh has bugs?  Really?

>     Tom Christiansen      tchrist@convex.com      convex!tchrist
> 
>     "UNIX was not designed to stop you from doing stupid things, because
>      that would also stop you from doing clever things." -- Doug Gwyn

   You're not cool enough to quote Doug Gwyn.  Really; it seems that
'csh sucks' in your opinion because it's not /bin/sh.  Care to try 
again?

   All in all, I'm sorely tempted to grab the existing csh source
and fix it, but I'll continue on our from-scratch project and 
do it that way.

-- 
 Jawaid Bazyar              |   Ask me about the GNO Multitasking Environment
 Procyon, Inc.              |   for the Apple IIgs!   
 bazyar@cs.uiuc.edu         |   P.O Box 620334
 --Apple II Forever!--      |   Littleton, CO 80162-0334   (303) 933-4649
Newsgroups: comp.unix.questions