[comp.unix.questions] use of set in a shell script

rhg@cpsolv.UUCP (Richard H. Gumpertz) (01/05/90)

I recently came across a shell script that, among other things, did the
following:
	OPTIONS=
	DIRS=
	for ARG
	do
		case "$ARG" in
		-*)	OPTIONS="$OPTIONS $ARG";;
		*)	DIRS="$DIRS $ARG";;
		esac
	done
	if test -z "$DIRS"; then
		DIRS="."
	fi
	set $DIRS
	find $@ -type f -exec ...
where the "..." in the find command indicates irrelevant stuff that I have
omitted.

My question is, why do the set in the next to last line?  Isn't the above
roughly equivalent to (but slower than)
	find $DIRS -type f -exec ...
where the set command has been deleted?  The only difference I can see is that
the version with the set command will make one more passing unquoting things,
which could have been avoided using
	set $DIRS
	find "$@" -type f -exec ...
Are there any other subtle differences that I missed?  Is there any way to
avoid the one pass of unquoting things that seems to remain in either case?

Is there a better way to write this whole thing?  (No, perl is not an
acceptable alternative in this case; perl scripts will be read but will not
solve my problem.)

-- 
  ==========================================================================
  | Richard H. Gumpertz    (913) 642-1777 or (816) 891-3561    rhg@CPS.COM |
  | Computer Problem Solving, 8905 Mohawk Lane, Leawood, Kansas 66206-1749 |
  ==========================================================================

maart@cs.vu.nl (Maarten Litmaath) (01/09/90)

In article <472@cpsolv.UUCP> rhg@cpsolv.UUCP (Richard H. Gumpertz) writes:
\...	set $DIRS
\	find $@ -type f -exec ...
\
\...  Isn't the above
\roughly equivalent to (but slower than)
\	find $DIRS -type f -exec ...
\where the set command has been deleted?  The only difference I can see is that
\the version with the set command will make one more passing unquoting things,
\which could have been avoided using
\	set $DIRS
\	find "$@" -type f -exec ...

What will happen to an unquoted variable?
1)	It is broken up into words at sequences of IFS.
2)	It is globbed.
No unquoting.

\Are there any other subtle differences that I missed?  Is there any way to
\avoid the one pass of unquoting things that seems to remain in either case?

optc=0
optv=

for i
do
	case $i in
	-*)
		optc=`expr $optc + 1`
		eval optv$optc='"$i"'
		optv="$optv \"\$optv$optc\""
		;;
	*)
		# you get the idea
	esac
done

eval set $optv		# restore the options EXACTLY
-- 
1755 EST, Dec 14, 1992: Henry Spencer is put on a one-way mission to the moon.|
  Maarten Litmaath @ VU Amsterdam:  maart@cs.vu.nl,  uunet!mcsun!botter!maart

gwc@root.co.uk (Geoff Clare) (01/16/90)

In article <5060@solo9.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>optc=0
>optv=
>
>for i
>do
>	case $i in
>	-*)
>		optc=`expr $optc + 1`
>		eval optv$optc='"$i"'
>		optv="$optv \"\$optv$optc\""
>		;;
>	*)
>		# you get the idea
>	esac
>done
>
>eval set $optv		# restore the options EXACTLY

A good attempt, Maarten, but there are a couple of big problems here.  

Firstly, the use of "expr" will be extremely slow for shells which don't
have "expr" built in (virtually all Bourne shells, I think).  There's no
need to use a separate variable for each argument, anyway.

Secondly, the final "set" command will not work correctly.  Suppose at
the start of the script $1 contains "-x".  This will end up as a
"set -x" command, which will turn on tracing mode in the shell, not
place "-x" in $1.  With some shells you can use "--" in a "set" command
to mark the end of the options, but a dummy first argument is more
portable.

Try this modified version:

optv=

for i
do
	case $i in
	-*)
		optv="$optv '$i'"
		;;
	*)
		# you get the idea
	esac
done

eval set X "$optv"; shift		# restore the options EXACTLY


-- 
Geoff Clare, UniSoft Limited, Saunderson House, Hayne Street, London EC1A 9HH
gwc@root.co.uk  (Dumb mailers: ...!uunet!root.co.uk!gwc)  Tel: +44-1-315-6600

maart@cs.vu.nl (Maarten Litmaath) (01/18/90)

In article <1197@root44.co.uk>,
	gwc@root.co.uk (Geoff Clare) writes:
\In article <5060@solo9.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
\>optc=0
\>optv=
\>
\>for i
\>do
\>	case $i in
\>	-*)
\>		optc=`expr $optc + 1`
\>		eval optv$optc='"$i"'
\>		optv="$optv \"\$optv$optc\""
\>		;;
\>	*)
\>		# you get the idea
\>	esac
\>done
\>
\>eval set $optv		# restore the options EXACTLY
\
\A good attempt, Maarten, but there are a couple of big problems here.  

A good attempt, Geoff, but there ... :-)

\Firstly, the use of "expr" will be extremely slow for shells which don't
\have "expr" built in (virtually all Bourne shells, I think).  There's no
\need to use a separate variable for each argument, anyway.

Wrong.  I didn't do that for nothing, you know.  See below.

\Secondly, the final "set" command will not work correctly.  Suppose at
\the start of the script $1 contains "-x".  This will end up as a
\"set -x" command, which will turn on tracing mode in the shell, not
\place "-x" in $1.  With some shells you can use "--" in a "set" command
\to mark the end of the options, but a dummy first argument is more
\portable.  [...]

You're right on this one.  In fact I meant to include a `-':

	eval set - $optv

\	case $i in
\	-*)
\		optv="$optv '$i'"
\...

What if $i were

	-'

(sic)?  Ridiculous, I know, but we're talking foolproof here.
The extra level of variables is one way to deal with nasty arguments, here's
another:

        case $i in
        -*\'*)   
                tmp=`echo x"$i" | sed -e 's/.//' -e "s/'/'\\\\\\\\''/g"`
                optv="$optv '$tmp'" 
                ;; 
        -*)      
                optv="$optv '$i'"
                ;; 
        esac

BTW, I tested this under SunOS 4.0.3c.
But wait a minute!  There's another problem: what if $i contains C escape
sequences and one is using that terrible System-V echo...? :-(
Hmm, one could always use the portable echo hack:
----------8<----------8<----------8<----------8<----------8<----------
: 'portable echo hack: do not interpret backslash escape sequences'
cat << EOF
$*
EOF
-- 
  What do the following have in common:  access(2), SysV echo, O_NONDELAY?  |
  Maarten Litmaath @ VU Amsterdam:  maart@cs.vu.nl,  uunet!mcsun!botter!maart