[net.unix] Bourne shell - $* and quoting

dce@mips.UUCP (David Elliott) (08/19/86)

In a recent posting, the following shell function was given:

	not() {
		$*
		if [ $? -eq 0 ]
		then return 1
		else return 0
		fi
	}

I'd like to get picky for a moment here for the benefit of all shell
programmers, particularly the beginners, as it pays to have good
programming habits.

The above function will not work as expected in cases where the arguments
contain characters special to the shell. For example,

	not echo '*' 

will actually expand * instead of just printing an asterisk. There are
many examples that can do the same thing.

The basic rule here is: $* is like goto; use it only when you can't get
the job done otherwise. The proper variable to use is "$@". For those of
you unfamiliar with "$@", the explanation is:

	$*	$1 $2 $3 ...
	"$*"	"$1 $2 $3 ..."
	"$@"	"$1" "$2" "$3" ...

In other words, "$@" prevents further processing of special characters, and
is therefore safe (except for a "bug" that says that if $* is empty, "$@"
will expand to "" instead of being empty).

As a side point, variables should always be quoted unless you know what is
in them. This means that any variable that could be set from data given
by a user should be placed in double quotes. It really scares me to see
something like:

	if [ x-q = x$1 ]
	...

since I could easily make $1 something like " -o 1 -eq 1". The really
funny part is that the author is very careful to put the x in front
of the arguments to avoid syntax errors. I think that the proper way
of giving arguments to 'test' is:

	[ " $string" = " value" ]

This looks good, is safe, and it works.

When I tell this to people, I sometimes get the excuse "I'm the only one
that will ever use it, and I would never do anything silly like having a
filename with a space or semicolon in it". Just remember that today's
beginners are tomorrow's wizards, and that little shell script you write
today may be something that you end up giving to tomorrow's beginners;
people that can stand in awe of your mistakes as well as your wizardry.

			David

A good evaluator is someone that can break something "without" looking at it.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (08/23/86)

In article <632@mips.UUCP> dce@mips.UUCP (David Elliott) writes:
[Nice explanation of the uses of $* "$*" and "$@".]

As the poster of the $* version of the "not" function, I applaud
attempts to improve it.  I used $* because I have gotten tired of
trying to get the shell to do the right thing in these contexts.
The "getopt" utility never could be made to work entirely correctly,
which helps explain why "getopts" is buil into the SVR3 shell.

jerryp@tektools.UUCP (Jerry Peek) (08/28/86)

In article <632@mips.UUCP> dce@mips.UUCP (David Elliott) writes:
> The basic rule here is: $* is like goto; use it only when you can't get
> the job done otherwise. The proper variable to use is "$@". For those of
> you unfamiliar with "$@", the explanation is:
> 
> 	$*	$1 $2 $3 ...
> 	"$*"	"$1 $2 $3 ..."
> 	"$@"	"$1" "$2" "$3" ...
> 
> In other words, "$@" prevents further processing of special characters, and
> is therefore safe (except for a "bug" that says that if $* is empty, "$@"
> will expand to "" instead of being empty).

I thought I'd add my two cents' worth -- on a related topic.

When they want to loop on all the commandline arguments, some people write
"for" loops this way, with $*:

	for var in $*
	do
	  blah blah
	  ...
	done

This gets you into the same problem that David mentioned... commandline
arguments will be re-scanned.  It's much better to use the default, which
*does* preserve the commandline arguments:

	for var
	do
	  ...

This is equivalent to:

	for var in "$@"
	do
	  ...

(except for the null-string bug that David mentioned).  BTW, lots of
Bourne shell manual pages say that 'for var' is equivalent to 'for var in $*',
but that's not true -- at least, not on any Bourne Shell I've seen.

--Jerry Peek, Tektronix, Inc.
US Mail:    MS 74-222, P.O. Box 500, Beaverton, OR 97077
uucp:       {allegra,decvax,hplabs,ihnp4,ucbvax}!tektronix!tektools!jerryp
CS,ARPAnet: jerryp%tektools@tektronix.csnet
Phone:      +1 503 627-1603