十大原因告诉你不要使用CShell

正当我满心欢喜,打算看看C Shell到底是何方神圣,并重新温习一下的时候,我居然发现了这篇文章,既然发现了,就转载一下吧。
正如作者最后说的一样,别重复犯他犯过的错误, ^_^

=================================================
Top Ten Reasons not to use the C shell
=================================================

Written by Bruce Barnett
With MAJOR help from
Peter Samuelson
Chris F.A. Johnson
Jesse Silverman
and of course Tom Christiansen

Updated:
September 22, 2001
November 26, 2002
July 12, 2004
February 27, 2006
October 3, 2006
January 17. 2007
November 22, 2007

Thanks to the following for finding typo’s etc.

Ed Morton

In the late 80’s, the C shell was the most popular interactive
shell. The Bourne shell was too “bare-bones.” The Korn shell had to
be purchased, and the Bourne Again shell wasn’t created yet.

I’ve used the C shell for years, and on the surface it has a
lot of good points. It has arrays (the Bourne shell only has one). It
has test(1), basename(1) and expr(1) built-in, while the Bourne shell
needed external programs. UNIX was hard enough to learn, and spending
months to learn two shells seemed silly when the C shell seemed
adequate for the job. So many have decided that since they were using
the C shell for their interactive session, why not use it for writing
scripts.

THIS IS A *BIG* MISTAKE.

Oh – it’s okay for a 5-line script. The world isn’t going to
end if you use it. However, many of the posters on USENET treat it as
such. I’ve used the C shell for very large scripts and it worked fine
in most cases. There are ugly parts, and work-arounds. But as your
script grows in sophistication, you will need more work-arounds and
eventually you will find yourself bashing your head against a wall
trying to work around the problem.

I know of many people who have read Tom Christiansen’s essay
about the C shell (http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/
), and they were not really convinced. A lot of Tom’s examples were
really obscure, and frankly I’ve always felt Tom’s argument wasn’t as
convincing as it could be. So I decided to write my own version of
this essay – as a gentle argument to a current C shell programmer from
a former C shell fan.

[Note – since I compare shells, it can be confusing. If the line starts
with a “%” then I’m using the C shell. If in starts with a “$” then it
is the Bourne shell.

————————————-
Top Ten reasons not to use the C shell
————————————-

1. The Ad Hoc Parser
2. Multiple-line quoting difficult
3. Quoting can be confusing and inconsistent
4. If/while/foreach/read cannot use redirection
5. Getting input a line at a time
6. Aliases are line oriented
7. Limited file I/O redirection
8. Poor management of signals and sub-processes
9. Fewer ways to test for missing variables
10. Inconsistent use of variables and commands.

1. The Ad Hoc Parser

The biggest problem of the C shell it its ad hoc parser.
Now this information won’t make you immediately switch shells.
But it’s the biggest reason to do so. Many of the other items listed
are based on this problem. Perhaps I should elaborate.

The parser is the code that converts the shell commands into
variables, expressions, strings, etc. High-quality programs have a
full-fledged parser that converts the input into tokens, verifies the
tokens are in the right order, and then executes the tokens.

The C shell does not do this. It parses as it executes. You
can have expressions in many types of instructions:

% if ( expression )
% set variable = ( expression )
% while ( expression )

They should be treated the same. They are not. You may find out that

% if ( 1 )

is fine, but

% if(1)

generates a syntax error. Or that the above works, while

% while(1)

fails.

You never know when you will find a new bug. As I write this
(September 2001) I ported a C shell script to another UNIX system. (It
was my .login script, okay? Sheesh!) Anyhow I got an error “Variable
name must begin with a letter” somewhere in the dozen files used when
I log in. I finally traced the problem down to the following “syntax”
error:

% if (! $?variable ) …

Which variable must begin with a letter? Give up? The solution –
you must add a space before the “!” character to fix the “error.” The
examples in the manual page don’t mention that spaces are required.
Sigh…

Here’s another one. I wanted to search for a string at the end
of a line, using grep. That is

% set var = “string”
% grep “$var$” < file Most shells treat this as % grep "string$" c” to the $a variable.
It only works if the current value does NOT have a space.
In other words

% set a = “a_b”
% set a = $a”\
c”

is fine. Changing “_” to a space causes a syntax error. Another
surprise. That’s the C shell – one never knows where the next surprise
will be.

3. Quoting can be confusing and inconsistent

The Bourne shell has three types of quotes:

“……..” – only $, `, and \ are special.
‘…….’ – Nothing is special (this includes the backslash)
\. – The next character is not special
(Exception: a newline)

That’s it. Very few exceptions. The C shell is another matter.
What works and what doesn’t is no longer simple and easy to understand.

As an example, look at the backslash quote. The Bourne shell uses the
backslash to escape everything except the newline. In the C shell, it
also escapes the backslash and the dollar sign. Suppose you want to enclose
$HOME in double quotes. Try typing:

% echo “$HOME”
/home/barnett

Logic tells us to put a backslash in front. So we try

% echo “\$HOME”
\/home/barnett

Sigh.
So there is no way to escape a variable in a double quote. What about
single quotes?

% echo ‘$HOME’
$HOME

works fine. But here’s another exception.

% echo MONEY$
MONEY$
% echo ‘MONEY$’
MONEY$
% echo “MONEY$”
Illegal variable name.

The last one is illegal. So adding double quotes CAUSES a syntax error.

With single quotes, “!” character is special, as is
the “~” character. Using single quotes (the strong quotes) the
command

% echo ‘!1’
1: Event not found.

will give you the error

A backslash is needed because the single quotes won’t quote the
exclamation mark. On some versions of the C shell,

echo hi!

works, but

echo ‘hi!’

doesn’t. A backslash is required in front:

echo ‘hi\!’

or if you wanted to put a ! before the word:

echo ‘\!hi’

Now suppose you type

% set a = “~”
% echo $a
/home/barnett
% echo ‘$a’
$a
% echo “$a”
~

The echo commands output THREE different values depending on the quotes.
So no matter what type of quotes you use, there are exceptions.
Those exceptions can drive you mad.

And then there’s dealing with spaces.

If you call a C shell script, and pass it an argument with a space:

% myscript “a b” c

Now guess what the following script will print.

#!/bin/csh -f
echo $#
set b = ( $* )
echo $#b

It prints “2” and then “3”. A simple = does not copy a variable
correctly if there are spaces involved. Double quotes don’t help.
It’s time to use the fourth form of quoting – which is only useful
when displaying (not set) the value:

% set b = ( $*:q )

Here’s another. Let’s saw you had nested backticks.
Some shells use $(program1 $(program2)) to allow this.
The C shell does not, so you have to use nested backticks.
I would expect this to be
`program1 \`program2\` `
but what works is the illogical
`program1 “program2“

Got it? It gets worse. Try to pass back-slashes to an alias
You need billions and billions of them. Okay. I exaggerate.
A little. But look at Dan Bernstein’s two aliases used to get quoting
correct in aliases:

% alias quote “/bin/sed -e ‘s/\\!/\\\\\!/g’ \\
-e ‘s/’\\\”/’\\\’\\\\\\\’\\\”/g’ \\
-e ‘s/^/’\”/’ \\
-e ‘s/”\$”/’\”/'”
% alias makealias “quote | /bin/sed ‘s/^/alias \!:1 /’ \!:2*”

You use this to make sure you get quotes correctly specified in aliases.

Larry Wall calls this backslashitis. What a royal pain.
Tick.. Tick.. Tick..

4. If/while/foreach/read cannot use redirection

The Bourne shell allows complex commands to be combined with pipes.
The C shell doesn’t. Suppose you want to choose an argument to grep.
Example:

% if ( $a ) then
% grep xxx
% else
% grep yyy
% endif

No problem as long as the text you are grepping is piped into the
script. But what if you want to create a stream of data in the script?
(i.e. using a pipe). Suppose you change the first line to be

% cat $file | if ($a ) then

Guess what? The file $file is COMPLETELY ignored. Instead, the
script use standard input of the script, even though you used a pipe on that line.
The only standard input the “if” command
can use MUST be specified outside of the script. Therefore what can be
done in one Bourne shell file has to be done in several C shell
scripts – because a single script can’t be used. The ‘while’ command
is the same way. For instance the following command outputs the time
with hyphens between the numbers instead of colons:

$ date | tr ‘:’ ‘ ‘ | while read a b c d e f g
$ do
$ echo The time is $d-$e-$f
$ done

You can use < as well as pipes. In other words, *ANY* command in the Bourne shell can have the data-stream redirected. That's because it has a REAL parser [rimshot]. Speaking of which... The Bourne shell allows you to combine several lines onto a single line as long as semicolons are placed between. This includes complex commands. For example - the following is perfectly fine with the Bourne shell: $ if true;then grep a;else grep b; fi This has several advantages. Commands in a makefile - see make(1) - have to be on one line. Trying to put a C shell "if" command in a makefile is painful. Also - if your shell allows you to recall and edit previous commands, then you can use complex commands and edit them. The C shell allows you to repeat only the first part of a complex command, like the single line with the "if" statement. It's much nicer recalling and editing the entire complex command. But that's for interactive shells, and outside the scope of this essay. 5. Getting input a line at a time Suppose you want to read one line from a file. This simple task is very difficult for the C shell. The C shell provides one way to read a line: % set ans = $< The trouble is - this ALWAYS reads from standard input. If a terminal is attached to standard input, then it reads from the terminal. If a file is attached to the script, then it reads the file. But what do you do if you want to specify the filename in the middle of the script? You can use "head -1" to get a line. but how do you read the next line? You can create a temporary file, and read and delete the first line. How ugly and extremely inefficient. On a scale of 1 to 10, it scores -1000. Now what if you want to read a file, and ask the user something during this? As an example - suppose you want to read a list of filenames from a pipe, and ask the user what to do with some of them? Can't do this with the C shell - $< reads from standard input. Always. The Bourne shell does allow this. Simply use $ read ans

This entry was posted in 脚本语言 and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *