home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #31 / NN_1992_31.iso / spool / comp / sys / apple2 / gno / 733 < prev    next >
Encoding:
Text File  |  1992-12-29  |  11.8 KB  |  461 lines

  1. Newsgroups: comp.sys.apple2.gno
  2. Path: sparky!uunet!cis.ohio-state.edu!zaphod.mps.ohio-state.edu!news.acns.nwu.edu!ftpbox!motsrd!mothost!lmpsbbs!lenz
  3. From: lenz@ssd.comm.mot.com (Doug Lenz)
  4. Subject: Why shouldn't you use csh?
  5. Organization: Motorola
  6. Date: Tue, 29 Dec 1992 15:04:55 GMT
  7. Message-ID: <1992Dec29.150455.8053@lmpsbbs.comm.mot.com>
  8. Sender: news@lmpsbbs.comm.mot.com (Net News)
  9. Nntp-Posting-Host: 145.1.54.2
  10. Lines: 449
  11.  
  12. Hi everyone!
  13.  
  14. After following the discussion about csh vs. any other REAL shell, I thought
  15. I'd include the "Why you shouldn't use csh FAQ"...
  16. (PS: Another thing to try, find a Sun workstation and do a man on csh.  The
  17.      man page TELLS you you shouldn't actually try to use csh for writing
  18.      scripts... What's the point?)
  19.  
  20. Here it is:
  21. ===========
  22.  
  23. From: Tom Christiansen <tchrist@pixel.Convex.COM>
  24. Date: Mon, 2 Nov 1992 22:14:18 GMT
  25. Newsgroups: comp.unix.shell,comp.unix.questions,news.answers
  26. Subject: Csh Programming Considered Harmful 
  27.  
  28. Archive-name: unix-faq/shell/csh-whynot
  29. Version: $Id: csh-faq,v 1.1 92/11/02 15:12:23 tchrist Exp Locker: tchrist $
  30.  
  31. The following periodic article answers in excruciating detail
  32. the frequently asked question "Why shouldn't I program in csh?".
  33. It is available for anon FTP from convex.com in /pub/csh.whynot .
  34.  
  35.  
  36.            Csh Programming Considered Harmful
  37.  
  38. Resolved: The csh is a tool utterly inadequate for programming, and
  39. its use for such purposes should be strictly banned.
  40.  
  41. I am continually shocked and dismayed to see people write test cases,
  42. install scripts, and other random hackery using the csh.  Lack of
  43. proficiency in the Bourne shell has been known to cause errors in /etc/rc
  44. and .cronrc files, which is a problem, because you *must* write these files
  45. in that language.
  46.  
  47. The csh is seductive because the conditionals are more C-like, so the path
  48. of least resistance if chosen and a csh script is written.  Sadly, this is
  49. a lost cause, and the programmer seldom even realizes it, even when they
  50. find that many simple things they wish to do range from cumbersome to
  51. impossible in the csh.
  52.  
  53.  
  54. FILE DESCRIPTORS
  55.  
  56. The most common problem encountered in csh programming is that
  57. you can't do file-descriptor manipulation.  All you are able to 
  58. do is redirect stdin, or stdout, or dup stderr into stdout. 
  59. Bourne-compatible shells offer you an abundance of more exotic
  60. possibilities.    
  61.  
  62. Writing Files
  63.  
  64. In the Bourne shell, you can open or dup random file descriptors.
  65. For example, 
  66.  
  67.     exec 2>errs.out
  68.  
  69. means that from then on, stderr goes into errs file.
  70.  
  71. Or what if you just want to throw away stderr and leave stdout
  72. alone?    Pretty simple operation, eh?
  73.  
  74.     cmd 2>/dev/null
  75.  
  76. Works in the Bourne shell.  In the csh, you can only make a pitiful 
  77. attempt like this:
  78.  
  79.     (cmd > /dev/tty) >& /dev/null
  80.  
  81. But who said that stdout was my tty?  So it's wrong.  This simple
  82. operation \fIcannot be done\fP in the csh.
  83.  
  84. Along these same lines, you can't direct error messages in csh 
  85. scripts out stderr as is considered proper.  In Bourne shell, you
  86. might say:
  87.  
  88.     echo "$0: cannot find $file" 1>&2
  89.  
  90. but in the csh, you can't redirect stdout out stderr, so you end
  91. up doing something silly like this:
  92.  
  93.     sh -c 'echo "$0: cannot find $file" 1>&2'
  94.  
  95. Reading Files
  96.  
  97. In the csh, all you've got is $<, which reads a line from your tty.  What
  98. if you've redirected stdin?  Tough noogies, you still get your tty, which 
  99. you really can't redirect.  Now, the read statement 
  100. in the Bourne shell allows you to read from stdin, which catches
  101. redirection.  It also means that you can do things like this:
  102.  
  103.     exec 3<file1
  104.     exec 4<file2
  105.  
  106. Now you can read from fd 3 and get lines from file1, or from file2 through
  107. fd 4.   In modern, Bourne-like shells, this suffices: 
  108.  
  109.     read some_var 0<&3
  110.     read another_var 0<&4
  111.  
  112. Although in older ones where read only goes from 0, you trick it:
  113.  
  114.     exec 5<&0  # save old stdin
  115.     exec 0<&3; read some_var
  116.     exec 0<&4; read another_var
  117.     exec 0<&5  # restore it
  118.  
  119.  
  120. Closing FDs
  121.  
  122. In the Bourne shell, you can close file descriptors you don't
  123. want open, like 2>&-, which isn't the same as redirecting it
  124. to /dev/null.
  125.  
  126. More Elaborate Combinations
  127.  
  128. Maybe you want to pipe stderr to a command and leave stdout alone.
  129. Not too hard an idea, right?  You can't do this in the csh as I
  130. mentioned in 1a.  In a Bourne shell, you can do things like this:
  131.  
  132.     exec 3>&1; grep yyy xxx 2>&1 1>&3 3>&- | sed s/file/foobar/ 1>&2 3>&-
  133.     grep: xxx: No such foobar or directory
  134.  
  135. Normal output would be unaffected.  The closes there were in case
  136. something really cared about all its FDs.  We send stderr to sed,
  137. and then put it back out 2.
  138.  
  139. Consider the pipeline:
  140.  
  141.     A | B | C
  142.  
  143. You want to know the status of C, well, that's easy: it's in $?, or
  144. $status in csh.  But if you want it from A, you're out of luck -- if
  145. you're in the csh, that is.  In the Bourne shell, you can get it, although
  146. doing so is a bit tricky.  Here's something I had to do where I ran dd's
  147. stderr into a grep -v pipe to get rid of the records in/out noise, but had
  148. to return the dd's exit status, not the grep's:
  149.  
  150.     device=/dev/rmt8
  151.     dd_noise='^[0-9]+\+[0-9]+ records (in|out)$'
  152.     exec 3>&1
  153.     status=`((dd if=$device ibs=64k 2>&1 1>&3 3>&- 4>&-; echo $? >&4) |
  154.         egrep -v "$dd_noise" 1>&2 3>&- 4>&-) 4>&1`
  155.     exit $status;
  156.  
  157.  
  158.  
  159. COMMAND ORTHOGONALITY
  160.  
  161. Built-ins
  162.  
  163. The csh is a horrid botch with its built-ins.  You can't put them
  164. together in many reasonable way.   Even simple little things like this:    
  165.  
  166.         % time | echo
  167.  
  168. which while nonsensical, shouldn't give me this message:
  169.  
  170.         Reset tty pgrp from 9341 to 26678
  171.  
  172. Others are more fun:
  173.  
  174.         % sleep 1 | while
  175.         while: Too few arguments.
  176.         [5] 9402
  177.         % jobs
  178.         [5]     9402 Done                 sleep |
  179.  
  180.  
  181. Some can even hang your shell.  Try typing ^Z while you're sourcing 
  182. something, or redirecting a source command.  Just make sure you have
  183. another window handy.
  184.  
  185. Flow control
  186.  
  187. You can't mix flow-control and commands, like this:
  188.     
  189.     who | while read line; do
  190.     echo "gotta $line"
  191.     done
  192.  
  193.  
  194. You can't combine multiline constructs in a csh using semicolons.
  195. There's no easy way to do this
  196.  
  197.     alias cmd 'if (foo) then bar; else snark; endif'
  198.  
  199.  
  200.  
  201.  
  202. Stupid parsing bugs
  203.  
  204. Certain reasonable things just don't work, like this:
  205.  
  206.     % kill -1 `cat foo`
  207.     `cat foo`: Ambiguous.
  208.  
  209. But this is ok:
  210.  
  211.     % /bin/kill -1 `cat foo`
  212.  
  213. If you have a stopped job:
  214.  
  215.     [2]     Stopped              rlogin globhost
  216.  
  217. You should be able to kill it with 
  218.  
  219.     % kill %?glob
  220.     kill: No match
  221.  
  222. but
  223.  
  224.     % fg %?glob
  225.  
  226. works.
  227.  
  228. White space can matter:
  229.  
  230.     if(expr)
  231.  
  232. may fail on some versions of csh, while
  233.  
  234.     if (expr)
  235.  
  236. works!
  237.  
  238.  
  239.  
  240. SIGNALS
  241.  
  242. In the csh, all you can do with signals is trap SIGINT.  In the Bourne
  243. shell, you can trap any signal, or the end-of-program exit.    For example,
  244. to blow away a tempfile on any of a variety of signals:
  245.  
  246.     $ trap 'rm -f /usr/adm/tmp/i$$ ;
  247.         echo "ERROR: abnormal exit";
  248.         exit' 1 2 3 15
  249.  
  250.     $ trap 'rm tmp.$$' 0   # on program exit
  251.  
  252.  
  253.  
  254. 6.  QUOTING
  255.  
  256. You can't quote things reasonably in the csh:
  257.  
  258.     set foo = "Bill asked, \"How's tricks?\""
  259.  
  260. doesn't work.  This makes it really hard to construct strings with
  261. mixed quotes in them.  In the Bourne shell, this works just fine. 
  262. In fact, so does this:
  263.  
  264.      cd /mnt; /usr/ucb/finger -m -s `ls \`u\``
  265.  
  266. Dollar signs cannot be escaped in double quotes in the csh.  Ug.
  267.  
  268.     set foo = "this is a \$dollar quoted and this is $HOME not quoted" 
  269.     dollar: Undefined variable.
  270.  
  271. You have to use backslashes for newlines, and it's just darn hard to
  272. get them into strings sometimes.
  273.  
  274.     set foo = "this \
  275.     and that";
  276.     echo $foo
  277.     this  and that
  278.     echo "$foo"
  279.     Unmatched ".  
  280.  
  281. Say what?  You don't have these problems in the Bourne shell, where it's
  282. just fine to write things like this:
  283.  
  284.     echo     'This is 
  285.          some text that contains
  286.          several newlines.'
  287.  
  288.  
  289. VARIABLE SYNTAX
  290.  
  291. There's this big difference between global (environment) and local
  292. (shell) variables.  In csh, you use a totally different syntax 
  293. to set one from the other.  
  294.  
  295. In Bourne shell, this 
  296.     VAR=foo cmds args
  297.  is the same as
  298.     (export VAR; VAR=foo; cmd args)
  299. or csh's
  300.     (setenv VAR;  cmd args)
  301.  
  302. You can't use :t, :h, etc on envariables.  Watch:
  303.     echo Try testing with $SHELL:t
  304.  
  305. It's really nice to be able to say
  306.     
  307.     ${PAGER-more}
  308. or
  309.     FOO=${BAR:-${BAZ}}
  310.  
  311. to be able to run the user's PAGER if set, and more otherwise.
  312. You can't do this in the csh.  It takes more verbiage.
  313.  
  314. You can't get the process number of the last background command from the
  315. csh, something you might like to do if you're starting up several jobs in
  316. the background.  In the Bourne shell, the pid of the last command put in
  317. the background is available in $!.
  318.  
  319. The csh is also flaky about what it does when it imports an 
  320. environment variable into a local shell variable, as it does
  321. with HOME, USER, PATH, and TERM.  Consider this:
  322.  
  323.     % setenv TERM '`/bin/ls -l / > /dev/tty`'
  324.     % csh -f
  325.  
  326. And watch the fun!
  327.  
  328.  
  329. EXPRESSION EVALUATION
  330.  
  331. Consider this statement in the csh:
  332.  
  333.  
  334.     if ($?MANPAGER) setenv PAGER $MANPAGER
  335.  
  336.  
  337. Despite your attempts to only set PAGER when you want
  338. to, the csh aborts:
  339.  
  340.     MANPAGER: Undefined variable.
  341.  
  342. That's because it parses the whole line anyway AND EVALUATES IT!
  343. You have to write this:
  344.  
  345.     if ($?MANPAGER) then
  346.     setenv PAGER $MANPAGER
  347.     endif
  348.  
  349. That's the same problem you have here:
  350.  
  351.     if ($?X && $X == 'foo') echo ok
  352.     X: Undefined variable
  353.  
  354. This forces you to write a couple nested if statements.  This is highly 
  355. undesirable 
  356. because it renders short-circuit booleans useless in situations like
  357. these.  If the csh were the really C-like, you would expect to be able
  358. to safely employ this kind of logic.  Consider the common C construct:
  359.  
  360.     if (p && p->member) 
  361.  
  362. Undefined variables are not fatal errors in the Bourne shell, so 
  363. this issue does not arise there.
  364.  
  365. While the csh does have built-in expression handling, it's not
  366. what you might think.  In fact, it's space sensitive.  This is an
  367. error
  368.  
  369.    @ a = 4/2
  370.  
  371. but this is ok
  372.  
  373.    @ a = 4 / 2
  374.  
  375.  
  376.  
  377. ERROR HANDLING
  378.  
  379. Wouldn't it be nice to know you had an error in your script before
  380. you ran it?   That's what the -n flag is for: just check the syntax.
  381. This is especially good to make sure seldom taken segments of code
  382. code are correct.  Alas, the csh implementation of this doesn't work.
  383. Consider this statement:
  384.  
  385.     exit (i)
  386.  
  387. Of course, they really meant
  388.  
  389.     exit (1)
  390.  
  391. or just
  392.  
  393.     exit 1
  394.  
  395. Either shell will complain about this.  But if you hide this in an if
  396. clause, like so:
  397.  
  398.     #!/bin/csh -fn
  399.     if (1) then
  400.     exit (i)
  401.     endif
  402.  
  403. The csh tells you there's nothing wrong with this script.  The equivalent
  404. construct in the Bourne shell, on the other hand, tells you this:
  405.  
  406.  
  407.     #!/bin/sh -n
  408.     if (1) then
  409.     exit (i)
  410.     endif
  411.  
  412.     /tmp/x: syntax error at line 3: `(' unexpected
  413.  
  414.  
  415.  
  416. RANDOM BUGS
  417.  
  418. Here's one:
  419.  
  420.     fg %?string
  421.     ^Z
  422.     kill  %?string
  423.     No match.
  424.  
  425. Huh? Here's another
  426.  
  427.     !%s%x%s
  428.  
  429. Coredump, or garbage.
  430.  
  431. If you have an alias with backquotes, and use that in backquotes in 
  432. another one, you get a coredump.
  433.  
  434. Try this:
  435.     % repeat 3 echo "/vmu*"
  436.     /vmu*
  437.     /vmunix
  438.     /vmunix
  439. What???
  440.  
  441.  
  442. While some vendors have fixed some of the csh's bugs (the tcsh also
  443. does much better here), most of its problems can never be solved
  444. because they're a result of braindead design decisions.  Do yourself
  445. a favor, and if you have to write a shell script, do it in the 
  446. Bourne shell.
  447.  
  448. -- 
  449.     Tom Christiansen      tchrist@convex.com      convex!tchrist
  450.     If you have ever seen the grim word "login:" on a screen, your mind
  451.     is now a wholly-owned subsidiary of The Death Star.
  452.                 John Woods in <14105@ksr.com> of comp.unix.bsd
  453.  
  454.  
  455. -- 
  456. =============================================================
  457. | Douglas Lenz   |   Friends don't let friends use MS-DOS   |
  458. -------------------------------------------------------------
  459. |           internet :   lenz@ssd.comm.mot.com              |
  460. =============================================================
  461.