[comp.unix.questions] * Question

wmartin@almsa-1.arpa (Will Martin -- AMXAL-RI) (11/30/88)

I have a query about filename generation/wildcard expansion. I have looked
in the man pages and in several UNIX books, and talked about this locally,
but haven't found an explanation. I've tested it on both Sys V (on a Sperry
Unisys) and 4.3 BSD (on a VAX 750). It seems to be a consistent difference
between sh and csh.

Given the situation where you are trying to redirect output to a directory
in some path structure, where you do have write access, but want to save
time in typing:

Say my login directory is /usr/wm. I also own a directory in another filesystem:
"/data/save-backups". I am sitting in /usr/wm and want to put some data into
that other directory, but want to avoid typing out "save-backups" all the time.
There is nothing else under /data that begins with "sa".

So I do this:

cat foo > /data/sa*/bar   or    cat foo >> /data/sa*/bar

Under "sh", no matter whether or not "bar" exists in that destination
directory, this won't work. I get the response:

sh: /data/sa*/bar: cannot create

The shell will not expand that "*" to fill out the directory name. I have to
type out the full "/data/save-backups/bar" pathname to get it to work.

Under "csh", if "bar" doesn't exist, I get: "/data/sa*/bar: No match",
but, if "bar" DOES exist, and I do either:

cat foo > /data/sa*/bar   or    cat foo >> /data/sa*/bar

it will work OK! So "csh" expands the wildcard * correctly in this case.

My query: Is this just a case where "sh" was broken and "csh" fixed it?
Or is there something else going on here? And, if "csh" lets this work
as shown, why does it NOT work when creating the file initially?

What I don't understand is why the expansion doesn't work under "sh".
You are sitting at the shell at this point. If the command was an "ls",
the "*" WOULD expand OK. Why doesn't it expand in this case?

Puzzled,
Will Martin
"wmartin@almsa-1.arpa"

logan@vsedev.VSE.COM (James Logan III) (11/30/88)

In article <17661@adm.BRL.MIL> wmartin@almsa-1.arpa (Will Martin -- AMXAL-RI) writes:
#I have a query about filename generation/wildcard expansion. I have looked
#in the man pages and in several UNIX books, and talked about this locally,
#but haven't found an explanation. I've tested it on both Sys V (on a Sperry
#Unisys) and 4.3 BSD (on a VAX 750). It seems to be a consistent difference
#between sh and csh.
#
#Given the situation where you are trying to redirect output to a directory
#in some path structure, where you do have write access, but want to save
#time in typing:
#
#Say my login directory is /usr/wm. I also own a directory in another filesystem:
#"/data/save-backups". I am sitting in /usr/wm and want to put some data into
#that other directory, but want to avoid typing out "save-backups" all the time.
#There is nothing else under /data that begins with "sa".
#
#So I do this:
#
#cat foo > /data/sa*/bar   or    cat foo >> /data/sa*/bar
#
#Under "sh", no matter whether or not "bar" exists in that destination
#directory, this won't work. I get the response:
#
#sh: /data/sa*/bar: cannot create
#
#The shell will not expand that "*" to fill out the directory name. I have to
#type out the full "/data/save-backups/bar" pathname to get it to work.
#

Every version of the Bourne shell that I have seen does not
interpret the filename expansion characters after a redirection
symbol.  I believe this is to prevent 

	cat foobar >/data/sa*/bar

from expanding into

	cat foobar >/data/sally/bar /data/sandy/bar

which would effectively be the same as

	cat foobar /data/sandy/bar >/data/sally/bar

which was probably not intended.

The Korn shell gets this right by expanding the wildcard only if one
file matches.  If more than one file matches, the wildcard is used
literally.

			-Jim

-- 
Jim Logan		logan@vsedev.vse.com
(703) 892-0002		uucp:	..!uunet!vsedev!logan
			inet:	logan%vsedev.vse.com@uunet.uu.net

ok@quintus.uucp (Richard A. O'Keefe) (11/30/88)

In article <17661@adm.BRL.MIL> wmartin@almsa-1.arpa (Will Martin -- AMXAL-RI) writes:
>Under "csh", if "bar" doesn't exist, I get: "/data/sa*/bar: No match",
>but, if "bar" DOES exist, and I do either:
>
>cat foo > /data/sa*/bar   or    cat foo >> /data/sa*/bar
>
>it will work OK! So "csh" expands the wildcard * correctly in this case.
>
>And, if "csh" lets this work
>as shown, why does it NOT work when creating the file initially?

When you give a wildcard pattern in Csh, there has to be at least one
thing in the file system which matches the complete pattern.  Suppose
you have an empty directory /data/savewhatsit/ and no other directories
matching /data/sa* .  Where then is there anything that matches the
**whole** pattern /data/sa*/bar ?

If you just want to save yourself a lot of typing, use a shell variable:

Csh:	set sa=/data/sa*
	echo works first time >$sa/bar

Sh:	sa=`echo /data/sa*`
	echo works first time >$sa/bar

Your message was /usr/spool/news/comp/unix/questions/7149 here.
In the Bourne shell, I did
	
	$ NEWS=/usr/spool/news
	$ cd $NEWS/comp/unix
	$ echo que*/7149
	$ echo $NEWS/co*/un*/que*/7149

and both echos worked.  This was on a Sequent, and the test worked in both
the "att" and the "ucb" universes.  For some real fun, I went to my home
directory and made a subdirectory zabbo containing a file fred.

	$ echo trouble >zab*/fred
	zab*/fred: cannot create
	$ x=zab*/fred
	$ echo $x
	zabbo/fred
	$ echo real trouble >$x
	zab*/fred: cannot create

Let's get this last one out of the way first.
	$ echo "$x"
	zab*/fred
We see from this that when a variable is assigned a value, that value
is not wild-card expanded ("globbed").  But if you use the value of the
variable later in a context where globbing is done, the value will be
globbed then.  That's why I wrote sa=`echo /data/sa*` above.

What about ">zab*/fred"?  Why didn't that work?  Well, you have to read
the Sh manual page with extreme attention to detail.  Several sorts of
"evaluation" happen to a command line.

	Command SUBSTITUTION
	    `command` is called and the output substituted
	Parameter SUBSTITUTION
	    $thingies are replaced by their values
	File Name GENERATION
	    "globbing", "wild-cards".  The manual page says clearly that
	    this happens after command and parameter substitution and
	    blank interpretation.

Now when we come to input/output redirection, we are told about
	<word
	>word
	>>word
and so on that "SUBSTITUTION" happens to the word before it is used.
That's *all* that happens; file name generation (globbing, wild-card
processing) is not done, and blank interpretation is not done either.
	$ y="BIG trouble"
	$ echo $y >$y
	$ ls
	BIG trouble
	fred
	$ ls $y
	BIG not found
	trouble not found
	$ rm "$y"

You can see why the Bourne shell works this way:  before command and
parameter substitution the "word" is *one* thing; blank interpretation
and file name generation could turn it into more than one thing, and
what would that mean?  The C shell handles it differently:

	% set y="BIG trouble"
	% echo $y >$y
	$y: Ambiguous.

As should be clear by now, assignment to keyword parameters is another
context where substitution is done but file name generation is not.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/01/88)

In article <17661@adm.BRL.MIL> wmartin@almsa-1.arpa (Will Martin -- AMXAL-RI) writes:
>cat foo > /data/sa*/bar

>What I don't understand is why the expansion doesn't work under "sh".

It was a deliberate design decision.  The target of > should be just one
word, not the potential multiple words that wildcard expansion might
produce.  To avoid unpleasant surprises, "sh" doesn't expand wildcards
in such contexts.

However, note that you can save typing using the BRL Bourne shell by
using the filename completion feature to automatically fill in the
rest of a pathname segment once you've typed the unambiguous prefix
for it.  I actually prefer this approach anyway, since I get to see
the full expansion before the command is executed.

guy@auspex.UUCP (Guy Harris) (12/01/88)

>And, if "csh" lets this work as shown, why does it NOT work when
>creating the file initially?

Because a "starname" must match something that already exists.  The
starname "foo/*/bar" will match all files named "bar" in any
subdirectory of "foo" (except those whose name begins with ".").  When
creating the file initially, it doesn't exist yet....