[OT] range() for unix shell

Paul Winkler bass at slinkp.com
Thu Mar 7 17:09:31 EST 2002


I've been taking some classes on various comp. subjects, including (currently)
unix/linux. We're currently doing shell scripting, which I've done before
but always kinda disliked, preferring our favorite snaky language.

Today, struck by the similarity between sh and python's for loops,
and facing an example which required us to generate a range of numbers
from boundary parameters, I wrote a sh range function. It works
just like python's range() function (allowing for
syntactic differences).

Of course now I'm expecting someone to leap forward and tell me
that some analogous feature is already built in to the shell,
but I haven't seen it yet.

Hoping it's useful to some of the unix/python people out there,
I'm posting my function below.

compare to familiar python examples:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(2, 10)
[2, 3, 4, 5, 6, 7, 8, 9]
>>> range(2, 10, 2)
[2, 4, 6, 8]
>>> range(10, -1)
[]
>>> range(5, -5, -3)
[5, 2, -1, -4]
>>> for i in range(4): print i
... 
0
1
2
3


here's a pdksh session (also tested in bash):

/home/train18$ range 10
0 1 2 3 4 5 6 7 8 9
/home/train18$ range 2 10
2 3 4 5 6 7 8 9
/home/train18$ range 2 10 2
2 4 6 8
/home/train18$ range 10 -1 

/home/train18$ range 5 -5 -3 
5 2 -1 -4
/home/train18$ for i in `range 4`; do echo $i; done
0
1
2
3


shell-is-weird-but-handy-ly-y'rs,
  Paul Winkler

# FUNCTION BEGINS HERE
range () {
        # written 2002 by Paul M. Winkler, Brooklyn, NY.
        # I hereby place the copyright to this program
        # in the public domain.

        # This is a translation of python's
        # range function into shell. Tested with
        # bash and pdksh. Doesn't work in ash (no "let").
        # Instead of returning an array (like python),
        # it returns a string that can be used as a sequence.
        # Example: for i in `range 10`; do echo $i; done
        
        # Defaults:
        let start=0
        let step=1
        case $# in
        1)      # start and step are default.
                let stop="$1"
                ;;
        2)      # Implicit 1 step.
                let start="$1"
                let stop="$2"
                ;;
        3)      # no default used
                let start="$1"
                let stop="$2"
                let step="$3"
                ;;
        *)
                cat << END_USAGE
Usage: $0 [start] stop [step]
Yields a sequence of integers N such that start <= N < stop.
If start is not given, it is 0 by default.
Successive values increase by step.
If step is not given, it is 1 by default.
END_USAGE
		return 1
                ;;
        esac
        
        # NOTE: all following tests have variables in quotes.
        # This is to avoid cryptic failures if $IFS happens
        # to be set to "-"
        if [ "$step" -eq 0 ] 
        then
                echo "Invalid step size 0. Must be a"
		echo "positive or negative integer."
                return 1
        fi      
        
        # Different comparisons apply if we're counting up or down.
        if [ "$step" -ge 1 ]
        then
                op='-lt'
        else
                op='-gt'
        fi

        result=""
        let thisnum=$start

        while [ "$thisnum" "$op" "$stop" ]
        do
                result="$result $thisnum"
                let thisnum=thisnum+step
        done
        echo "$result"
        }



# END



More information about the Python-list mailing list