[GRASS-dev] Re: grass-dev Digest, Vol 3, Issue 8

Glynn Clements glynn at gclements.plus.com
Thu Jul 6 00:06:33 EDT 2006


Michael Barton wrote:

> When I get back, should I go through and get rid of all occurrences of eval
> exec?
> 
> I noticed that Cedric introduced a different form "exec $cmd [list arg arg
> art...]

If you have individual arguments, there should be no eval, just:

	exec $cmd $arg1 $arg2

etc.

If you have a list of arguments, then the correct idiom is:

	eval [concat [list exec $cmd $arg1 $arg2] $otherargs]
or:
	eval exec [concat [list $cmd $arg1 $arg2] $otherargs]
or:
	eval exec $cmd [concat [list $arg1 $arg2] $otherargs]

Although the last one is only correct if it is known that $cmd cannot
contain spaces; don't use it if $cmd might be an absolute pathname (on
Windows, it might be in the "Program Files" directory).

The main point here is that eval will treat each argument as a list. 
If any of the arguments /aren't/ lists, they have to be inserted into
a list so that they aren't themselves treated as lists and broken into
multiple elements if they contain spaces.

An example:

	set mapname "elevation.dem"
	set filename "/cygdrive/c/Documents and Settings/glynn/My Documents/elev.ppm"
	exec r.out.ppm input=$mapname output=$filename

The actual command will be:

	argv[0] = 'r.out.ppm'
	argv[1] = 'input=elevation.dem'
	argv[2] = 'output=/cygdrive/c/Documents and Settings/glynn/My Documents/elev.ppm'

No problem there, but with "eval exec ...":

	eval exec r.out.ppm input=$mapname output=$filename

$filename would get split into multiple arguments:

	argv[0] = 'r.out.ppm'
	argv[1] = 'input=elevation.dem'
	argv[2] = 'output=/cygdrive/c/Documents'
	argv[3] = 'and'
	argv[4] = 'Settings/glynn/My'
	argv[5] = 'Documents/elev.ppm'

A similar issue applies to shell scripts; several of the scripts in
etc/gm/scripts use "eval exec ..." unnecessarily, e.g. r.recode.file:

	eval `exec r.recode input=$GIS_OPT_INPUT output=$GIS_OPT_OUTPUT < $GIS_OPT_RULES_FILE` 

This will fail if $GIS_OPT_RULES_FILE contains spaces. The correct
formulation is:

	exec r.recode "input=$GIS_OPT_INPUT" "output=$GIS_OPT_OUTPUT" < "$GIS_OPT_RULES_FILE"

Also, note the use of double quotes around each argument; this
prevents it being split if the variable contains spaces (this is one
signficant difference between shell syntax and Tcl syntax; Tcl won't
split variable expansions unless forced to through the use of eval). 

Quotes aren't strictly necessary for the input= and output= options,
because map names aren't allowed to contain spaces. But it won't hurt
either, and it's good practice to put quotes around all variable
expansions unless you specifically want the expansion to be split into
words.

They are necessary for the redirect filename, as that could contain
spaces.

However, this won't work work for the scripts which construct a
command using e.g. cmd="$cmd something=$GIS_OPT_SOMETHING", because
Bourne-shell doesn't have any support for lists (recent versions of
bash have arrays, but those are highly non-portable). There isn't any
robust solution to that issue, although using single quotes will help.

-- 
Glynn Clements <glynn at gclements.plus.com>




More information about the grass-dev mailing list