[comp.unix.questions] Help with strings in Bourne shell

rudolf@oce.orst.edu (Jim Rudolf) (04/24/89)

If I have a Bourne script called 'foo' and I call it with the arguments:
   foo "color = red" "size = big"
then from within foo they will be read as:
   $1 = "color = red"
   $2 = "size = big"
However, I want to read from stdin (or maybe a redirected pipe), and I
can't get it to work no matter what strange combination of quotes I use!
I would like to do something like:
   read args
   for i in $args
   .
   .
so I can process each string in turn.  Why can't I get this to work?
Do command line arguments get treated differently than arguments read
from within the script?  Any suggestions would be greatly appreciated.

Thanks,

Jim Rudolf

----------------------------------------------------------------------------
Internet: rudolf@oce.orst.edu                 "All opinions herein are mine" 
UUCP: {tektronix,hp-pcd}!orstcs!oce.orst.edu!rudolf
----------------------------------------------------------------------------

logan@vsedev.VSE.COM (James Logan III) (04/25/89)

In article <10166@orstcs.CS.ORST.EDU> rudolf@oce.orst.edu (Jim Rudolf) writes:
# If I have a Bourne script called 'foo' and I call it with the arguments:
#    foo "color = red" "size = big"
# then from within foo they will be read as:
#    $1 = "color = red"
#    $2 = "size = big"
# However, I want to read from stdin (or maybe a redirected pipe), and I
# can't get it to work no matter what strange combination of quotes I use!
# I would like to do something like:
#    read args
#    for i in $args
#    .
#    .
# so I can process each string in turn.

If I have interpreted your question correctly, you want to read
the lines 

	color = red
	size = big

into your script and have each line read into a variable.


To do this, use the construct:

	while read DEFINITION; do
		echo "$DEFINITION";
		.
		.
		.
	done;


BTW, you can also read from a specific file by redirecting the
input to the read command like this:

	INPUTFILE="some_file";

	while read DEFINITION <$INPUTFILE; do
		echo "$DEFINITION";
		.
		.
		.
	done;


			-Jim

-- 
Jim Logan                           logan@vsedev.vse.com
VSE Software Development Lab        uucp:  ..!uunet!vsedev!logan
(703) 329-4654                      inet:  logan%vsedev.vse.com@uunet.uu.net

dce@Solbourne.COM (David Elliott) (04/25/89)

In article <1493@vsedev.VSE.COM> logan@vsedev.VSE.COM (James Logan III) writes:
>BTW, you can also read from a specific file by redirecting the
>input to the read command like this:
>
>	INPUTFILE="some_file";
>
>	while read DEFINITION <$INPUTFILE; do
>		echo "$DEFINITION";
>		.
>		.
>		.
>	done;

Watch out for this one.  It's very important to note that in the
"standard" shells (those that come with most commercial UNIXes),
the redirection causes a subshell to be spawned to run the loop.

The result is that any variables set inside the loop will not
have the same values when the loop is exited.  So, if the input
data are used to set variables to be used later in the program,
this won't work.

A typical trick is

	exec 3<&0 0<"$INPUTFILE"
	while read DEFINITION
	do
		echo "$DEFINITION"
	done
	exec 0<&3

The first exec makes fd 3 a duplicate of fd 0 (stdin), and
redirects stdin.  The second exec changes fd 0 back to what
it was.

-- 
David Elliott		dce@Solbourne.COM
			...!{boulder,nbires,sun}!stan!dce

mchinni@pica.army.mil (Michael J. Chinni, SMCAR-CCS-E) (04/26/89)

Jim Rudolf <rudolf@oce.orst.edu> writes:
> If I have a Bourne script called 'foo' and I call it with the arguments:
>    foo "color = red" "size = big"
> then from within foo they will be read as:
>    $1 = "color = red"
>    $2 = "size = big"
> However, I want to read from stdin (or maybe a redirected pipe), and I
> can't get it to work no matter what strange combination of quotes I use!
> I would like to do something like:
>    read args
>    for i in $args
>    .
>    .
> so I can process each string in turn.  Why can't I get this to work?
> Do command line arguments get treated differently than arguments read
> from within the script?  Any suggestions would be greatly appreciated.
As to how to do this, others have given answers. As to why this doesn't work,
read on.

The problem is with the "for" loop. The "for" construct uses a list of arguments
which are words separated by tabs/spaces. Therefore, "for" will split your
arguments into their individual words.  Given your command line arguments:
	echo $1
	echo $2
will produce
	color = red
	size = big

I know of no way to do what you want using a "for" loop.

/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
			    Michael J. Chinni
      Chief Scientist, Simulation Techniques and Workplace Automation Team
	US Army Armament Research, Development, and Engineering Center
 User to skeleton sitting at cobweb    () Picatinny Arsenal, New Jersey  
   and dust covered terminal and desk  () ARPA: mchinni@pica.army.mil
    "System been down long?"           () UUCP: ...!uunet!pica.army.mil!mchinni
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

friedl@vsi.COM (Stephen J. Friedl) (04/26/89)

In article <1493@vsedev.VSE.COM>, logan@vsedev.VSE.COM (James Logan III) writes:
> 
> BTW, you can also read from a specific file by redirecting the
> input to the read command like this:
> 
> 	while read DEFINITION < inputfile; do
> 		echo "$DEFINITION";
> 		# other stuff
> 	done;

This is a common mistake.  The redirection applies to only the
read command, and every time the while hits the read, the input
file is opened anew and the same first line is read over and
over (and the while never terminates).  Try:

	while read line < /etc/passwd ; do
		echo $line
	done

and you'll learn your encrypted root password quite well.

The {Bou,Ko}rn shells support piping and redirection into and
out of control flow, so things like:

	while read foo ; do
		stuff here
	done < inputfile
or
	while condition ; do
		stuff here
	done > outputfile
or
	grep stuff file | while read line ; do .... ; done

all do things that make sense.

     Steve

-- 
Stephen J. Friedl / V-Systems, Inc. / Santa Ana, CA / +1 714 545 6442 
3B2-kind-of-guy   / friedl@vsi.com  / {attmail, uunet, etc}!vsi!friedl

As long as Bush is in office, you'll never see Nancy Reagan in *my* .sig.

rikki@macom1.UUCP (R. L. Welsh) (04/27/89)

From article <19311@adm.BRL.MIL>, by mchinni@pica.army.mil (Michael J. Chinni, SMCAR-CCS-E):
> Jim Rudolf <rudolf@oce.orst.edu> writes:
>> If I have a Bourne script called 'foo' and I call it with the arguments:
>>    foo "color = red" "size = big"
>> then from within foo they will be read as:
>>    $1 = "color = red"
>>    $2 = "size = big"
>> However, I want to read from stdin (or maybe a redirected pipe), and I
>> can't get it to work no matter what strange combination of quotes I use!
>> I would like to do something like:
>>    read args
>>    for i in $args
>>    .
>>    .
>> so I can process each string in turn.  Why can't I get this to work?

Try:
	\"x = value\"

This worked for me -- you need to get the " passed to the for line. 

From vn Thu Apr 27 10:12:11 1989
Subject: Re: I Live
Newsgroups: dfw.test
References: <4498@psuvax1.cs.psu.edu>

From article <4498@psuvax1.cs.psu.edu>, by Elvis@cup.portal.com (The King):
> Just testing.
> Elvis
Hi Elvis.

From vn Thu Apr 27 10:13:21 1989
Subject: Re: test
Newsgroups: dc.test
References: <12610@jhunix>
Distribution: dc

From article <12610@jhunix>, by tester@jhunix (tester):
> testing
Testing followup.

-- 
	- Rikki	(UUCP: grebyn!macom1!rikki)

rikki@macom1.UUCP (R. L. Welsh) (04/27/89)

> Jim Rudolf <rudolf@oce.orst.edu> writes:
...
>> However, I want to read from stdin (or maybe a redirected pipe), and I
>> can't get it to work no matter what strange combination of quotes I use!
>> I would like to do something like:
>>    read args
>>    for i in $args

Try:
	\"x = value\"

This worked for me -- you need to get the " passed to the for line. 

-- 
	- Rikki	(UUCP: grebyn!macom1!rikki)

stever@tree.UUCP (Steve Rudek) (04/28/89)

In article <870@marvin.Solbourne.COM>, dce@Solbourne.COM (David Elliott) writes:
> 	exec 3<&0 0<"$INPUTFILE"
> 	while read DEFINITION
> 	do
> 		echo "$DEFINITION"
> 	done
> 	exec 0<&3
> 
> The first exec makes fd 3 a duplicate of fd 0 (stdin), and
> redirects stdin.  The second exec changes fd 0 back to what
> it was.
I've never seen anything quite like this.  It is beautiful, exciting and
rather bizarre; shell at its best! :-) I can guess that you're creating
a file descriptor 3 *strictly* to preserve the (address??) of stdin.  But the
0<"$INPUTFILE" bit boggles my mind.  How is this different from just <$INPUTFILE
I guess the leading "exec" is important in understanding this?  Could you
explain?

By the way, how would:
  while read DEFINITION <$INPUTFILE 
  do
  done

differ from: 
  while read DEFINITION
  do
  done <$INPUTFILE

Or wouldn't it?


If not too much trouble, I'd appreciate a *copy* of any responses via email
since I really have a considerable interest in the answer and fear I might
otherwise miss a newsgroup response.
----------
Steve Rudek  {ucbvax!ucdavis!csusac OR ames!pacbell!sactoh0} !tree!stever

logan@vsedev.VSE.COM (James Logan III) (04/28/89)

In article <1493@vsedev.VSE.COM> I wrote:
# BTW, you can also read from a specific file by redirecting the
# input to the read command like this:
# 
# 	INPUTFILE="some_file";
# 
# 	while read DEFINITION <$INPUTFILE; do
# 		echo "$DEFINITION";
# 		.
# 		.
# 		.
# 	done;

Oooops, I screwed up!  What I wrote above will read the first line
again and again!  The best solution is:

 	INPUTFILE="some_file";
 
	exec 4<$INPUTFILE;
 	while read DEFINITION 0<&4; do
 		echo "$DEFINITION";
 		.
 		.
 		.
 	done;
	exec 4<&-;

since the alternative construct:

 	INPUTFILE="some_file";
 
 	while read DEFINITION; do
 		echo "$DEFINITION";
 		.
 		.
 		.
 	done <$INPUTFILE;

has the side-effect of redirecting the input of every command
invoked from within the while loop.   

			-Jim

-- 
Jim Logan                           logan@vsedev.vse.com
VSE Software Development Lab        uucp:  ..!uunet!vsedev!logan
(703) 329-4654                      inet:  logan%vsedev.vse.com@uunet.uu.net

logan@vsedev.VSE.COM (James Logan III) (04/28/89)

In article <870@marvin.Solbourne.COM> dce@Solbourne.com (David Elliott) writes:
# In article <1493@vsedev.VSE.COM> logan@vsedev.VSE.COM (James Logan III) writes:
# >BTW, you can also read from a specific file by redirecting the
# >input to the read command like this:
# >
# >	INPUTFILE="some_file";
# >
# >	while read DEFINITION <$INPUTFILE; do
# >		echo "$DEFINITION";
# >		.
# >		.
# >		.
# >	done;

This construct has a BIG problem.  I corrected it in a previous
posting.  

# 
# A typical trick is
# 
# 	exec 3<&0 0<"$INPUTFILE"
# 	while read DEFINITION
# 	do
# 		echo "$DEFINITION"
# 	done
# 	exec 0<&3
# 
# The first exec makes fd 3 a duplicate of fd 0 (stdin), and
# redirects stdin.  The second exec changes fd 0 back to what
# it was.

This has the side effect of redirecting stdin for every command
invoked inside the while loop.  (Take a look at my corrected
posting.)  Besides, it has the same effect as the simpler
construct: 

	while read DEFINITION; do
		echo "$DEFINITION";
	done <$INPUTFILE;


Under System V (on 3B2, Altos, and XENIX at least) the variable
"DEFINITION" is not set in a sub-shell.  To do this you would
have to do something silly like:  

	while `read DEFINITION`; do
		echo "$DEFINITION";
	done;

Perhaps this isn't the case in BSD UNIX.

			-Jim

-- 
Jim Logan                           logan@vsedev.vse.com
VSE Software Development Lab        uucp:  ..!uunet!vsedev!logan
(703) 329-4654                      inet:  logan%vsedev.vse.com@uunet.uu.net

ka@june.cs.washington.edu (Kenneth Almquist) (04/30/89)

rudolf@oce.orst.edu (Jim Rudolf) writes:
> If I have a Bourne script called 'foo' and I call it with the arguments:
>    foo "color = red" "size = big"
> then from within foo they will be read as:
>    $1 = "color = red"
>    $2 = "size = big"
> However, I want to read from stdin (or maybe a redirected pipe), and I
> can't get it to work no matter what strange combination of quotes I use!
> I would like to do something like:
>    read args
>    for i in $args
>    .
>    .
> so I can process each string in turn.  Why can't I get this to work?
> Do command line arguments get treated differently than arguments read
> from within the script?  Any suggestions would be greatly appreciated.

Presumably you want to read in a single line with multiple quoted strings
in it.  The read command doesn't handle quoted strings.  (The shell manual
page lies.)  One possible solution is to use the eval command:

	read line || exit 0	# exit if end of file
	eval "set $line"	# parse line
	for i in "$@"
	...

Let's work through an example.  Assume that the input line is

	"color = red" "size = big"

Then the read command will store the line in the variable "line".  The
second command will run the eval command with a single argument, the
string

	set "color = red" "size = big"

The set command assigns its arguments to the positional parameters, so
you will get

	$1 = "color = red"
	$2 = "size = big"

The for loop will then step through the positional parameters.

Several warnings about this.  First, if the first argument to set begins
with a minus or plus sign, it will be interpreted as an option.  Second,
this won't work if the number of arguments to set is zero.  Third, the
set command changes the positional parameters, making the original arguments
to the shell procedure inaccessible.  And fourth, if the argument to "set"
is not syntactically correct, the shell procedure will terminate.
				Kenneth Almquist

davidsen@steinmetz.ge.com (Wm. E. Davidsen Jr) (05/03/89)

In article <275@tree.UUCP> stever@tree.UUCP (Steve Rudek) writes:

| By the way, how would:
|   while read DEFINITION <$INPUTFILE 
|   do
|   done
| 
| differ from: 
|   while read DEFINITION
|   do
|   done <$INPUTFILE
| 
| Or wouldn't it?

  Redirection on the read command would open and read the INPUTFILE
every time throught the loop (ie. read 1st line forever). On the done
line the redirect causes all commands in the loop to read their stdin
from INPUTFILE.

  If you want to read successive lines from a file and process them, but
not redirect stdin, here's a technique (I invented it myself, but I
doubt that it's unique).

	while read name 0<&3
	do
	  : whatever here
	  : stdin is still tty
	done 3< $INPUTFILE

  This works with SysV sh as well as ksh (I didn't try BSD, sorry).
-- 
	bill davidsen		(wedu@crd.GE.COM)
  {uunet | philabs}!steinmetz!crdos1!davidsen
"Stupidity, like virtue, is its own reward" -me