Portable POSIX shell alternative to GNU seq(1)?
An alternative to awk
is bc
:
seq() (first=$1 incr=$2 last=$3
echo "for (i = $first; i <= $last; i+=$incr) i" | bc -l
)
An advantage is that you're not limited by the size/resolution of your CPU's doubles:
$ seq '(2^200)' '(2^100)' '(2^200+2^102)'
1606938044258990275541962092341162602522202993782792835301376
1606938044258990275541962092342430253122431223184289538506752
1606938044258990275541962092343697903722659452585786241712128
1606938044258990275541962092344965554322887681987282944917504
1606938044258990275541962092346233204923115911388779648122880
But beware of line wrapping when the numbers get too big:
$ seq '(2^500)' '(2^100)' '(2^500+2^101)'
32733906078961418700131896968275991522166420460430647894832913680961\
33796404674554883270092325904157150886684127560071009217256545885393\
053328527589376
32733906078961418700131896968275991522166420460430647894832913680961\
33796404674554883270092325904157150886684127560071010484907146113622\
454825230794752
32733906078961418700131896968275991522166420460430647894832913680961\
33796404674554883270092325904157150886684127560071011752557746341851\
856321934000128
According to the open group POSIX awk supports BEGIN
, therefore it can be done in awk
:
awk -v MYEND=6 'BEGIN { for(i=1;i<=MYEND;i++) print i }'
Where -v MYEND=6
would stand for the assignment as in the first argument to seq
. In other words, this works too:
END=6
for i in `awk -v MYEND=$END 'BEGIN { for(i=1;i<=MYEND;i++) print i }'`; do
echo $i
done
Or even with three variables (start, increment and end):
S=2
I=2
E=12
for i in `awk -v MYS=$S -v MYI=$I -v MYE=$E 'BEGIN { for(i=MYS;i<=MYE;i+=MYI) print i }'`; do
echo $i
done
Extra Solaris note: On Solaris /usr/bin/awk
is not POSIX compliant, you need to use either nawk
or /usr/xpg4/bin/awk
on Solaris.
On Solaris, you probably want to set /usr/xpg4/bin
early in PATH if you are running a POSIX compliant script.
Reference answer:
- awk hangs on Solaris
I ended up writing one in ksh a while ago; it's not as bullet-proof as it could be, because I wrote it for myself, but it should be a good start. Not sure it'd work with pure Bourne shell, as your Q is currently tagged, because of the $(( )) syntax.
#!/usr/bin/ksh
start=1
end=1
step=1
case $# in
0)
echo Usage: $0 '[Start [Step]] End'
exit 0
;;
1)
end=$1
;;
2)
start=$1
end=$2
;;
3)
start=$1
step=$2
end=$3
;;
esac
# quick & dirty validations
if [ $step -eq 0 ]
then
exit 1
fi
if [ $step -gt 0 ]
then
if [ $start -gt $end ]
then
exit 2
fi
else
if [ $start -lt $end ]
then
exit 3
fi
fi
i=$start
if [ $step -gt 0 ]
then
while [ $i -le $end ]
do
printf "%d\n" $i
i=$(( i + step ))
done
else
while [ $i -ge $end ]
do
printf "%d\n" $i
i=$(( i + step ))
done
fi