Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Best way to change IFS for a for loop in Bourne shell

278 views
Skip to first unread message

Frederick W. Wheeler

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to

I have a for loop in a Bourne shell script that needs to use a colon
separated list. I'm wondering if I'm changing and reseting the IFS
right because my solution seems kludgy. Does anyone know a better
way?

list="foo -opt1:bar -opt2:baz -opt3"

origIFS=$IFS
IFS=':'
for x in $list; do
# reset IFS here because commands in the loop rely on IFS being the default
IFS=$origIFS
echo $x
# more stuff in here that relies on IFS being the default
done
# need to reset IFS here in case list is empty and for loop not executed
IFS=$origIFS

I was very surprised that the following did not work. The error
message I get is "do unexpected".

IFS=':' for x in $list; do
echo $x
done

I don't think it matters, but just in case, my system is:

% uname -a
SunOS seurat 5.6 Generic_105181-03 sun4u sparc
% which sh
sh is /usr/bin/sh

Thanks,
Fred Wheeler

--
Fred Wheeler
wheeler (at) cipr.rpi.edu
www.cipr.rpi.edu/wheeler

Angela Schwarz

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to

I'm not sure if I unterstand what you want to do. I expect the
following: first x="foo -opt1", second x="bar -opt2", ...

What about replacing all spaces first by perhaps "+" and all ":" by
sopaces. In your loop, you can than replace the "+" by space. So you
don't have to change your IFS.

for y in `echo $list | sed -e 's/ /+/g' -e 's/:/ /g'`; do
x=`echo $y | sed 's/+/ /g'`
echo $x
done

Angela

Frederick W. Wheeler

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to

Let me it clear that the following loop works. It does what I want it
to do, printing:

foo -opt1
bar -opt2
baz -opt3

It just seems kludgy to have to set and restore IFS the way I do. I'm
looking for a simple way to get IFS-: applied to the $list variable
expansion in the for loop and nowhere else.

whe...@cipr.no_spam.rpi.edu (Frederick W. Wheeler) writes:
> list="foo -opt1:bar -opt2:baz -opt3"
>
> origIFS=$IFS
> IFS=':'
> for x in $list; do
> # reset IFS here because commands in the loop rely on IFS being the default
> IFS=$origIFS
> echo $x
> # more stuff in here that relies on IFS being the default
> done
> # need to reset IFS here in case list is empty and for loop not executed
> IFS=$origIFS

Thanks,

Donn Cave

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to
whe...@cipr.no_spam.rpi.edu (Frederick W. Wheeler) writes:
| I have a for loop in a Bourne shell script that needs to use a colon
| separated list. I'm wondering if I'm changing and reseting the IFS
| right because my solution seems kludgy. Does anyone know a better
| way?
|
| list="foo -opt1:bar -opt2:baz -opt3"
|
| origIFS=$IFS
| IFS=':'
| for x in $list; do
| # reset IFS here because commands in the loop rely on IFS being the default
| IFS=$origIFS
| echo $x
| # more stuff in here that relies on IFS being the default
| done
| # need to reset IFS here in case list is empty and for loop not executed
| IFS=$origIFS

You're sure right about kludgy, but the only thing I can suggest (I do
this not infrequently myself) is to replace the argument list:

origIFS=$IFS
set $list
IFS=$origIFS
for x
do ...

If you can't discard the actual argument list at this point, and your shell
doesn't have array capabilities anywhere else, the only other approaches I
can think of are even worse. You could get something to work along the lines
of replace : with newlines, and replace "for x" with "while read x", but it
would be no great step forward.

| I was very surprised that the following did not work. The error
| message I get is "do unexpected".
|

| IFS=':' for x in $list; do

| echo $x
| done

That syntax is to my knowledge only valid for assignment of environment
variables for a command statement, which "for" is not.

Donn Cave, University Computing Services, University of Washington
do...@u.washington.edu

Ken Pizzini

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to
On 02 Mar 1999 08:35:18 -0500,

Frederick W. Wheeler <whe...@cipr.no_spam.rpi.edu> wrote:
>
>I have a for loop in a Bourne shell script that needs to use a colon
>separated list. I'm wondering if I'm changing and reseting the IFS
>right because my solution seems kludgy. Does anyone know a better
>way?
>
>list="foo -opt1:bar -opt2:baz -opt3"
>
>origIFS=$IFS
>IFS=':'
>for x in $list; do
> # reset IFS here because commands in the loop rely on IFS being the default
> IFS=$origIFS
> echo $x
> # more stuff in here that relies on IFS being the default
>done
># need to reset IFS here in case list is empty and for loop not executed
>IFS=$origIFS

Depending on whether or not you need to access the pre-existing argument
list at or after this point in the script, you might be able to use:
origIFS="$IFS"
IFS=':'
set -- $list
IFS="$origIFS"
for x; do


echo $x
# more stuff in here that relies on IFS being the default
done

# more stuff here that relies on IFS being the default

Not a huge win in terms of complexity, but at least all the IFS
games are localized in one place, rather than scattered hither
and yon.

--Ken Pizzini

Dale Hagglund

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to
do...@u.washington.edu (Donn Cave) writes:

> | I was very surprised that the following did not work. The error
> | message I get is "do unexpected".
> |

> | IFS=':' for x in $list; do

> | echo $x
> | done
>
> That syntax is to my knowledge only valid for assignment of environment
> variables for a command statement, which "for" is not.

I just happened to think of this off the top of my head. It seems to work.

for f in `IFS=: ; set -- $PATH ; echo "$@"`
do
echo $f
done

I'm not sure this counts as better or worse than any of the other
solutions I've seen. It does avoid the problem of saving/restoring
IFS, at the expense of a subshell.

Dale.

brian hiles

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to
Dale Hagglund <r...@best.com> wrote:

> do...@u.washington.edu (Donn Cave) writes:
> for f in `IFS=: ; set -- $PATH ; echo "$@"`
> do
> echo $f
> done

My favorite scripting idiom for the above situation is:

IFS=: eval "set -- \$list; IFS='$IFS'" # "list" is your colon-delimited list var

No temporary variables holding IFS; no setting and resetting of
IFS per a scope of code; no unnecessary processes or process
environments or external software.... Now that the hard work is
done:

for list_element
do # do anything here; IFS is always set to default ...
done

-Brian

Dale Hagglund

unread,
Mar 2, 1999, 3:00:00 AM3/2/99
to
brian hiles <b...@rainey.blueneptune.com> writes:

> My favorite scripting idiom for the above situation is:

> IFS=: eval "set -- \$list; IFS='$IFS'"

> No temporary variables holding IFS; no setting and resetting of


> IFS per a scope of code; no unnecessary processes or process
> environments or external software....

Hey, I like this one. Mine had other problems to, I think, if the
various fields happened to contain whitespace.

Why do you need the IFS assignment in the eval string though? It
seems to work fine without it for me, as in

IFS=: eval 'set -- $list'

Dale.

Donn Cave

unread,
Mar 3, 1999, 3:00:00 AM3/3/99
to
brian hiles <b...@rainey.blueneptune.com> writes:
|Dale Hagglund <r...@best.com> wrote:
|> do...@u.washington.edu (Donn Cave) writes:
|> for f in `IFS=: ; set -- $PATH ; echo "$@"`
|> do
|> echo $f
|> done

I didn't write any of that, contrary to the apparent attribution.
The problem with the suggested usage is that the output of the `` gets
split, again, this time on white space. If there is any white space
in there, you lose. /etc/passwd is a better example of this than PATH.

| My favorite scripting idiom for the above situation is:
|

| IFS=: eval "set -- \$list; IFS='$IFS'" # "list" is your colon-delimited list var
|

| No temporary variables holding IFS; no setting and resetting of
| IFS per a scope of code; no unnecessary processes or process

| environments or external software.... Now that the hard work is
| done:

OK, but it seems to me like a lot of trouble to avoid a temporary variable,
compared to the IFS based approach the original poster was already using.
I guess there's always going to be some difference of opinion on questions
maintainability and elegance, maybe I react too strongly to "eval".

brian hiles

unread,
Mar 3, 1999, 3:00:00 AM3/3/99
to
Dale Hagglund <r...@best.com> wrote:

> brian hiles <b...@rainey.blueneptune.com> writes:
>> My favorite scripting idiom for the above situation is:
>> IFS=: eval "set -- \$list; IFS='$IFS'"
> Hey, I like this one. Mine had other problems to, I think, if the
> various fields happened to contain whitespace.

Thank you. For every submission of a good idea I have made to
c.u.s., I assure you I have received three from it!

> Why do you need the IFS assignment in the eval string though? It
> seems to work fine without it for me, as in

> IFS=: eval 'set -- $list'

Technically, one does not need to include the "eval" or for that
matter the reassignment of the IFS variable; however, the side-efect
of the above will be to change the IFS variable--and the scripting
idiom I submitted exists to transparently parse field delimiters
without making these. As a scripting idiom, one merely need to
"substitute the parameters" and not worry about consequences.
Command verbosity is made up by benignity.

The "eval" is there for the necessity of resetting the IFS value
to the original value_and_ performing a "set ..." _in the same
instruction_.

P.S. Unfortunately the above will not work as written in bash, as
Chet Ramey has not seen fit to implement the documented feature "special
shell builtins" as he cannot think of why anyone would what to use
this facility. Hmmm.

-Brian

Chet Ramey

unread,
Mar 3, 1999, 3:00:00 AM3/3/99
to
In article <7biap9$gg$1...@remarQ.com>,
brian hiles <b...@rainey.blueneptune.com> wrote:

>P.S. Unfortunately the above will not work as written in bash, as
>Chet Ramey has not seen fit to implement the documented feature "special
>shell builtins" as he cannot think of why anyone would what to use
>this facility. Hmmm.

Don't be deliberately disingenuous. This feature is available when
bash is in posix mode, and is documented.

--
``The lyf so short, the craft so long to lerne.'' - Chaucer
( ``Discere est Dolere'' -- chet)

Chet Ramey, Case Western Reserve University Internet: ch...@po.CWRU.Edu

d.phi...@rochester.gov.uk

unread,
Mar 4, 1999, 3:00:00 AM3/4/99
to
In article <36DC09...@sdm.de>,
angela....@sdm.de wrote:

> Frederick W. Wheeler wrote:
> >
> > I have a for loop in a Bourne shell script that needs to use a colon
> > separated list. I'm wondering if I'm changing and reseting the IFS
> > right because my solution seems kludgy. Does anyone know a better
> > way?
> >
> > list="foo -opt1:bar -opt2:baz -opt3"
> >
> > origIFS=$IFS
> > IFS=':'
> > for x in $list; do
> > # reset IFS here because commands in the loop rely on IFS being the
default
> > IFS=$origIFS
> > echo $x
> > # more stuff in here that relies on IFS being the default
> > done
> > # need to reset IFS here in case list is empty and for loop not executed
> > IFS=$origIFS
> >
> > I was very surprised that the following did not work. The error
> > message I get is "do unexpected".
> >
> > IFS=':' for x in $list; do
> > echo $x
> > done
> >
> > I don't think it matters, but just in case, my system is:
> >
> > % uname -a
> > SunOS seurat 5.6 Generic_105181-03 sun4u sparc
> > % which sh
> > sh is /usr/bin/sh
> >
> > Thanks,
> > Fred Wheeler
> >
> > --
> > Fred Wheeler
> > wheeler (at) cipr.rpi.edu
> > www.cipr.rpi.edu/wheeler
>
> I'm not sure if I unterstand what you want to do. I expect the
> following: first x="foo -opt1", second x="bar -opt2", ...
>
> What about replacing all spaces first by perhaps "+" and all ":" by
> sopaces. In your loop, you can than replace the "+" by space. So you
> don't have to change your IFS.
>
> for y in `echo $list | sed -e 's/ /+/g' -e 's/:/ /g'`; do
> x=`echo $y | sed 's/+/ /g'`
> echo $x
> done
>
> Angela

You could cheat, and do something like ;

IFS=":"
set -A array1 $list
for x in ${array1[*]}
do
echo x
done
IFS=" "

Cheers

davey

>

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

0 new messages