[comp.unix.shell] for loops

edh@ux.acs.umn.edu (Merlinus Ambrosius) (04/03/91)

In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
set to some number, and I'd like to go through a loop $FILES times.  Can
this be done in sh?

Thanks!
		--eric

smazu@ameris.UUCP (Steven P. Mazurek) (04/04/91)

In article <3693@ux.acs.umn.edu>, edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
> .....Say I have $FILES set to some number, and I'd like to go through
> a loop $FILES times.  Can this be done in sh?
> 

You don't mention the type of shell your using, but for
Bourne investigate the command expr(1).

	while [ $FILES -ne 0 ]		# use strings if its more comfortable
	do  .... things ....
	    FILES=`expr $FILES - 1`
	done



-- 
Steven P. Mazurek	 | Email: {uunet,bcr,lbesac}!ameris!smazu
Ameritech Services	 | 
Schaumburg, IL USA 60010 | Phone : (708) 605-2858

jik@athena.mit.edu (Jonathan I. Kamens) (04/04/91)

In article <3693@ux.acs.umn.edu>, edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
|> In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
|> set to some number, and I'd like to go through a loop $FILES times.  Can
|> this be done in sh?

Yes.  The trick is getting from the number to that many items that you can put
into a for loop.  Something like this

	FILES=10
	for i in `jot $FILES`; do
		echo -n "*"
	done; echo ""

will print ten asterisks and then a newline, if your echo supports "-n"
and you have "jot".  If you don't have "jot", you can get it from
/help/dist on lilac.berkeley.edu.  Alternatively, you can use awk to do
the counting:

	FILES=10
	for i in `echo $FILES | awk '{for(i=0;i<$1+0;i++){print i}; exit}'`; do
		echo -n "*"
	done; echo ""

which will print the same as the loop above.

  You could also use expr and test to decrement the variable by one on each
pass through the loop and check if it's zero, but that's very inefficient
compared to the methods above, which require only one fork to set up the count
for the entire loop.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

pefv700@perv.pe.utexas.edu (04/04/91)

In article <3693@ux.acs.umn.edu>, edh@ux.acs.umn.edu (Merlinus Ambrosius) writes...
>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>set to some number, and I'd like to go through a loop $FILES times.  Can
>this be done in sh?
> 
>Thanks!
>		--eric

How about either

while [ $FILES -gt 0 ]
do
    # body
    FILES=`expr $FILES - 1`
done

or

i=0
while [ $i -lt $FILES ]
do
    # body
    i=`expr $i + 1`
done

Chris

miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) (04/04/91)

In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>set to some number, and I'd like to go through a loop $FILES times.  Can
>this be done in sh?

POSIX states that sh(1) should be able to evaluate expressions, 
so you can do something like
while [ $FILES != 0 ]
do
	echo -n '* '
	FILES=$[$FILES - 1]
done

But I haven't seen a sh anywhere that is already capable of doing this
(not even the one I am writing myself for Minix... yet.).
Maybe somebody knows if a new ksh can do this?

-- 
+===============================+============================================+
|                               |                                            |
| Miquel van Smoorenburg,       |  It's nice to be important,                |
| miquels@maestro.htsa.aha.nl   |  but it's more important to be nice.       |
|                               |                                            |
+===============================+============================================+

jimr@hplsdv7.COS.HP.COM (Jim Rogers) (04/05/91)

/ hplsdv7:comp.unix.shell / edh@ux.acs.umn.edu (Merlinus Ambrosius) /  4:14 pm  Apr  2, 1991 /
In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
set to some number, and I'd like to go through a loop $FILES times.  Can
this be done in sh?

Thanks!
		--eric
----------
Try the following:


#!/bin/sh

foo=10
count=0

while [ $count -lt $foo ]
do
	echo $count
	count=`expr $count + 1`
done

The "expr" command evaluates arguments as an expression and writes the
result to stdout.


Jim Rogers
Hewlett-Packard Company
Colorado Springs, Colorado

rad@genco.bungi.com (Bob Daniel) (04/05/91)

In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>set to some number, and I'd like to go through a loop $FILES times.  Can
>this be done in sh?
>

for i in $FILES
do
	ls -l $i
	....  or whatever to do with that file
done


If FILES="/etc/inittab /etc/passwd /etc/mnttab etc...", each file will be 
treated individually in the example above.

spicer@tci.UUCP (Steve Spicer) (04/05/91)

In article <2816@maestro.htsa.aha.nl> miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) writes:
>In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>>set to some number, and I'd like to go through a loop $FILES times.  Can
>>this be done in sh?
>
>POSIX states that sh(1) should be able to evaluate expressions, 
>so you can do something like
>while [ $FILES != 0 ]
>do
>	echo -n '* '
>	FILES=$[$FILES - 1]
>done
>
>But I haven't seen a sh anywhere that is already capable of doing this
>(not even the one I am writing myself for Minix... yet.).
>Maybe somebody knows if a new ksh can do this?

	ksh allows this (which I tested before posting)
	$ integer i=5
	$ while (( i > 0 )) ; do
	>	print $i
	>	(( i = i - 1 ))
	>done
	5
	4
	3
	2
	1
	$

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Steven Spicer/spicer@tci.bell-atl.com

Is your design so simple that there are obviously no deficiencies,
or so complicated that there are no obvious deficiencies?
	-- suggested by a quote from C.A.R. Hoare

asg@sage.cc.purdue.edu (Bruce Varney) (04/05/91)

In article <2816@maestro.htsa.aha.nl> miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) writes:
}In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
}>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
}>set to some number, and I'd like to go through a loop $FILES times.  Can
}>this be done in sh?
}
}POSIX states that sh(1) should be able to evaluate expressions, 
}while [ $FILES != 0 ]
}do
}	echo -n '* '
}	FILES=$[$FILES - 1]
}done
}
No, but the new bash (version 1.07) can! ;-)
}
---------
                                   ###             ##
Courtesy of Bruce Varney           ###               #
aka -> The Grand Master                               #
asg@sage.cc.purdue.edu             ###    #####       #
PUCC                               ###                #
;-)                                 #                #
;'>                                #               ##

dattier@vpnet.chi.il.us (David W. Tamkin) (04/05/91)

miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) wrote in
<2816@maestro.htsa.aha.nl>:

| POSIX states that sh(1) should be able to evaluate expressions, 
| so you can do something like
| while [ $FILES != 0 ]
| do
| 	echo -n '* '
| 	FILES=$[$FILES - 1]
| done

| But I haven't seen a sh anywhere that is already capable of doing this
| (not even the one I am writing myself for Minix... yet.).
| Maybe somebody knows if a new ksh can do this?

Even an old ksh can do 
let FILES = $FILES - 1
or
(( FILES = FILES - 1 ))

David Tamkin  PO Box 7002  Des Plaines IL  60018-7002  dattier@vpnet.chi.il.us
GEnie:D.W.TAMKIN  CIS:73720,1570  MCIMail:426-1818  708 518 6769  312 693 0591

]) (04/06/91)

In article <2816@maestro.htsa.aha.nl> miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) writes:
>In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>>set to some number, and I'd like to go through a loop $FILES times.  Can
>>this be done in sh?
>
>POSIX states that sh(1) should be able to evaluate expressions, 
>so you can do something like
>while [ $FILES != 0 ]
>do
>	echo -n '* '
>	FILES=$[$FILES - 1]
>done

#### ksh fragment ####
while [ $FILES -ge 0 ]
do
	echo "$FILES"
	((FILES -= 1))	# or ((FILES = $FILES - 1))
done
#### ksh fragment ####

...Kris
-- 
Kristopher Stephens, | (408-746-6047) | krs@uts.amdahl.com | KC6DFS
Amdahl Corporation   |                |                    |
     [The opinions expressed above are mine, solely, and do not    ]
     [necessarily reflect the opinions or policies of Amdahl Corp. ]

]) (04/06/91)

In article <2816@maestro.htsa.aha.nl> miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) writes:
>In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>>set to some number, and I'd like to go through a loop $FILES times.  Can
>>this be done in sh?
>
>POSIX states that sh(1) should be able to evaluate expressions, 
>so you can do something like
>while [ $FILES != 0 ]
>do
>	echo -n '* '
>	FILES=$[$FILES - 1]
>done
>
>But I haven't seen a sh anywhere that is already capable of doing this
>(not even the one I am writing myself for Minix... yet.).
>Maybe somebody knows if a new ksh can do this?

#### ksh fragment ####
while [ $FILES -gt 0 ]
do
        echo "$FILES"
        ((FILES -= 1))  # or ((FILES = $FILES - 1))
done
#### ksh fragment ####

...Kris
-- 
Kristopher Stephens, | (408-746-6047) | krs@uts.amdahl.com | KC6DFS
Amdahl Corporation   |                |                    |
     [The opinions expressed above are mine, solely, and do not    ]
     [necessarily reflect the opinions or policies of Amdahl Corp. ]

robtu@itx.isc.com (Rob Tulloh) (04/06/91)

miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) writes:

>In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>>set to some number, and I'd like to go through a loop $FILES times.  Can
>>this be done in sh?

>POSIX states that sh(1) should be able to evaluate expressions, 
>so you can do something like
>while [ $FILES != 0 ]
>do
>	echo -n '* '
>	FILES=$[$FILES - 1]
>done

>But I haven't seen a sh anywhere that is already capable of doing this
>(not even the one I am writing myself for Minix... yet.).
>Maybe somebody knows if a new ksh can do this?

In the version of ksh running on the RS/6000, the following is possible:

typeset -i FILES=10
while [ $i -gt 0 ] ; do
	# body of loop
	FILES=FILES-1
done

or the following:

typeset -i FILES=10
while ((i > 0)) ; do
	# body of loop
	FILES=FILES-1
done

or if you want to be more specific:

typeset -i FILES=10
while ((i > 0)) ; do
	# body of loop
	let FILES=FILES-1
done

The typeset -i forces FILES to be interpreted as an integer and allows
you to forgo the use of $var and the let syntax. let is a shell builtin
which uses (( and )) as shorthand much the same way test uses [ and ].

Rob Tulloh
--
INTERACTIVE Systems Corp.         Tel: (512) 343 0376 Ext. 116
9442 Capital of Texas Hwy. North  Fax: (512) 343 0376 Ext. 161 (not a typo!)
Arboretum Plaza One, Suite 700    Net: robertt@isc.com (polled daily)
Austin, Texas 78759               GEnie: R.TULLOH (polled monthly)

ask@cbnews.att.com (Arthur S. Kamlet) (04/06/91)

In article <robtu.670879801@mexia> robtu@itx.isc.com (Rob Tulloh) writes:
>In the version of ksh running on the RS/6000, the following is possible:
>
>typeset -i FILES=10
>while [ $i -gt 0 ] ; do
>	# body of loop
>	FILES=FILES-1
>done
>
>The typeset -i forces FILES to be interpreted as an integer and allows
>you to forgo the use of $var and the let syntax.

In fact,  my ksh  comes with an exported alias for typeset -i
named "integer"

So I can say:

 integer FILES=10
-- 
Art Kamlet  a_s_kamlet@att.com  AT&T Bell Laboratories, Columbus

rich@pencil.cs.missouri.edu (Rich Winkel) (04/06/91)

jik@athena.mit.edu (Jonathan I. Kamens) writes:
]In article <3693@ux.acs.umn.edu>, edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
]|> In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
]|> set to some number, and I'd like to go through a loop $FILES times.  Can
]|> this be done in sh?
]Yes.  The trick is getting from the number to that many items that you can put
]into a for loop.  Something like this

]	FILES=10
]	for i in `jot $FILES`; do
]		echo -n "*"
]	done; echo ""

]will print ten asterisks and then a newline, if your echo supports "-n"
]and you have "jot".  If you don't have "jot", you can get it from
]/help/dist on lilac.berkeley.edu.  ....

How about this variation for those who don't have jot:
	FILES=10
	for i in `head -$FILES /usr/dict/words`
		echo -n "*"
	done

I guess someone's going to tell me now that 'head' isn't standard unix?

Rich

chet@odin.INS.CWRU.Edu (Chet Ramey) (04/07/91)

In article <2816@maestro.htsa.aha.nl> miquels@maestro.htsa.aha.nl (Miquel van Smoorenburg) writes:

$ POSIX states that sh(1) should be able to evaluate expressions, 
$ so you can do something like
$ while [ $FILES != 0 ]
$ do
$ 	echo -n '* '
$ 	FILES=$[$FILES - 1]
$ done
$ 
$ But I haven't seen a sh anywhere that is already capable of doing this

Bash can do this, at least as of version 1.07.

Chet
-- 
Chet Ramey			  Internet: chet@po.CWRU.Edu
Case Western Reserve University	  NeXT Mail: chet@macbeth.INS.CWRU.Edu

``Now,  somehow we've brought our sins back physically -- and they're pissed.''

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (04/07/91)

In article <rich.670890780@pencil> rich@pencil.cs.missouri.edu (Rich Winkel) writes:
> 	for i in `head -$FILES /usr/dict/words`
> 		echo -n "*"
> 	done

  for i in `yes | head -$FILES`
    echo -n "*"
  done

But for this I'd probably just write

  yes '*' | head -$FILES | tr -d '
  '

And let's not forget the poor man's count script:

  #!/bin/sh
  ( echo $1 ; yes | head -$2 | sed 's/y/p1+/' ) | dc

$1 is the starting number, $2 how many numbers to produce. It even works
for floating-point---try count 3.14159 10.

---Dan

ronald@robobar.co.uk (Ronald S H Khoo) (04/07/91)

rich@pencil.cs.missouri.edu (Rich Winkel) writes:

> 	FILES=10
> 	for i in `head -$FILES /usr/dict/words`

Hee hee.  Neat hack.  I got a feeling that my boss would be very unhappy if
I used that in production code though :-)  But I like it.
 
> I guess someone's going to tell me now that 'head' isn't standard unix?

Yes, but unless you're head'ing more than one file, sed <n>q is the same
as head -n, so you could have avoided the use of that disclaimer, had
you said:

 	for i in `sed ${FILES}q /usr/dict/words`

Also, if no shell variables are involved, sed 10q is less to type than
head -10 :-)
-- 
Ronald Khoo <ronald@robobar.co.uk> +44 81 991 1142 (O) +44 71 229 7741 (H)

dattier@vpnet.chi.il.us (David W. Tamkin) (04/08/91)

rich@pencil.cs.missouri.edu (Rich Winkel) wrote in <rich.670890780@pencil>:

| I guess someone's going to tell me now that 'head' isn't standard unix?

It's standard in BSD but don't look for it in System V.

David Tamkin  PO Box 7002  Des Plaines IL  60018-7002  dattier@vpnet.chi.il.us
GEnie:D.W.TAMKIN  CIS:73720,1570  MCIMail:426-1818  708 518 6769  312 693 0591

"Parker Lewis Can't Lose" mailing list --
 relay: flamingo-request@esd.sgi.com    digest: flamingo-request@ddsw1.mcs.com

kiyun@mirror.tmc.com (KiYun Roe) (04/08/91)

In article <27850:Apr700:08:2591@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>And let's not forget the poor man's count script:
>
>  #!/bin/sh
>  ( echo $1 ; yes | head -$2 | sed 's/y/p1+/' ) | dc

I like this!  I've been looking for a simple way to do this.  I didn't
realize that there was a command like yes.  Is it BSD, System V, or
both?  Anyway, I don't think you need to bring sed into the pipe:

  #!/bin/sh
  ( echo $1 ; yes p1+ | head -$2 ) | dc
-- 
----
KiYun Roe	kiyun@mirror.TMC.COM
Mirror Systems	2067 Massachusetts Avenue  Cambridge, MA  02140
Telephone:	617-661-0777

pfinkel@cbnewsb.cb.att.com (paul.d.finkel) (04/10/91)

In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>set to some number, and I'd like to go through a loop $FILES times.  Can
>this be done in sh?
>
>Thanks!
>		--eric
Sure!

	count=0
	while $count -lt $files
		do
		:
		count=`expr $count + 1`
		done
I think this will do it!

-- 
  Family motto: Semper ubi, sub ubi. mail: attmail!pfinkel  
  "My name is Fink, whaddaya think, I press pants for nothing?"
   (Punch line to corny joke that my father always told!)

pfinkel@cbnewsb.cb.att.com (paul.d.finkel) (04/10/91)

In article <1991Apr9.185256.18650@cbfsb.att.com> pfinkel@cbnewsb.cb.att.com (paul.d.finkel) writes:
>In article <3693@ux.acs.umn.edu> edh@ux.acs.umn.edu (Merlinus Ambrosius) writes:
>>In sh, I'd like to do something like a BASIC for loop.  Say I have $FILES
>>set to some number, and I'd like to go through a loop $FILES times.  Can
>>this be done in sh?
>>
>>Thanks!
>>		--eric
>Sure!
>
>	count=0
>	while $count -lt $files
>		do
>		:
>		count=`expr $count + 1`
>		done
>I think this will do it!

Escuse mio, per favor. You need brackets around your test statetment:

	while [ $count -lt $files ]
-- 
  Family motto: Semper ubi, sub ubi. mail: attmail!pfinkel  
  "My name is Fink, whaddaya think, I press pants for nothing?"
   (Punch line to corny joke that my father always told!)

stuart@amc.com (Stuart Poulin) (04/11/91)

In article <54239@mirror.tmc.com> kiyun@mirror.UUCP (KiYun Roe) writes:
>In article <27850:Apr700:08:2591@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>>And let's not forget the poor man's count script:
>>
>>  #!/bin/sh
>>  ( echo $1 ; yes | head -$2 | sed 's/y/p1+/' ) | dc
>
>I like this!  I've been looking for a simple way to do this.  I didn't
>realize that there was a command like yes.  Is it BSD, System V, or
>both?  Anyway, I don't think you need to bring sed into the pipe:
>
>  #!/bin/sh
>  ( echo $1 ; yes p1+ | head -$2 ) | dc
>-- 
>----

Hey! cool idea.  How about using bc and adding a step argument:

# print range from $1 to $2 step $3
echo " for ( i = $1; i <= $2 ; i += $3 ) { i } " | bc

You can even step fractional steps - try it: 1 10 .1
And you can even swap "<="  around to count down. Wow!

Stuart Poulin                             DNS: stuart@amc.com
Applied Microsystems Corporation         UUCP: amc-gw!stuart
Redmond, Washington  98073               Dial: 800-ASK-4AMC,206-882-2000 

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (04/12/91)

In article <1991Apr10.212216.24238@amc.com> stuart@tfatf.amc.com (Stuart Poulin) writes:
> In article <54239@mirror.tmc.com> kiyun@mirror.UUCP (KiYun Roe) writes:
> >I like this!  I've been looking for a simple way to do this.  I didn't
> >realize that there was a command like yes.  Is it BSD, System V, or
> >both?

I believe it appeared in v7, but I'm not sure how portable the argument
is.

> Hey! cool idea.  How about using bc and adding a step argument:

No need for bc. Step can be positive or negative here.

#!/bin/sh
# pmcount start [ num [ step ] ], num default 10, step default 1
step=`echo ${3-1} | sed 's/-/_/g'`
( echo $1 ; yes | head -${2-10} | sed 's/y/p'$step'+/' ) | dc

I suppose the yes and head could be replaced by echo and a more complex
sed script.

---Dan

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (04/13/91)

In article <13418:Apr1203:39:4891@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
> In article <1991Apr10.212216.24238@amc.com> stuart@tfatf.amc.com (Stuart Poulin) writes:
> > In article <54239@mirror.tmc.com> kiyun@mirror.UUCP (KiYun Roe) writes:
> > >I like this!  I've been looking for a simple way to do this.  I didn't
> > >realize that there was a command like yes.  Is it BSD, System V, or
> > >both?
> I believe it appeared in v7, but I'm not sure how portable the argument
> is.

Correction: I looked it up, and AT&T didn't have it in 1985. Oh, well.

---Dan

pfeiffer@cix.cict.fr (Daniel Pfeiffer) (04/16/91)

What I don't like about the expr example, is the number of times
we need to fork, since it's not a builtin.  The following, adapted
from a proposal one or two months back is a bit hard to understand,
(documented at the end) but should be lots quicker:

$ FILES=5
$ for i in `echo "[p 1 + d $FILES!<P] sP 1 lP x" | dc`
> do
> echo $i
> done
1
2
3
4
5
$ 

Someone also proposed a comparable script for awk, but, given that awk
is a rather huge language, I suppose that dc is quicker. Alternately,
for better readability, you might say

count() {
	echo "[p 1 + d $1 !<P] sP 1 lP x" | dc
}

for i in `count 5`

You can also twiddle the first 1 to change the increment, or the
second 1 to change the starting point, as in the following script:

#! /bin/sh
if [ ! "$1" ]; then
	echo "usage:  count number
	count beg end [step]">&2
	exit 1
elif [ $# = 1 ]; then
	set 1 $1
fi
# Program [ print top, increment top, duplicate, push end, pop and compare
#	two top values, reexecute P if not past end ]
# store program as P, push beginning, load P, execute it
echo "[p ${3-1} + d $2 !<P] sP $1 lP x" | dc

--
-- Daniel Pfeiffer				<pfeiffer@cix.cict.fr>
-- Tolosa (Toulouse), Midi-Pyrenees, Europe	<pfeiffer@irit.fr>
-- "Beware - polyglot esperantist"		<pfeiffer@frcict81.bitnet>
--

      N
    _---_
   /	 \	NEWS, it goes around the world.
W (-------) E	(sorry, my bitmap doesn't have a world-class resolution)
   \_	_/
     ---