BenH
BenH

Reputation: 71

shell - iterating a loop for a (validated) supplied number or range of numbers

I need to accept input from user (i.e. 'read').

This input can be either a single positive number or a range of numbers (in the form X-Y ).

I then need to validate this input and perform an iterative loop through the range of numbers (or just once in the case of only X).

examples:

1) User supplies: "8" or "8-" Loop runs only a single time supplying the number 8

2) User supplies: "13-22"

Loop runs 11 times (13 through 22) referencing the number 13.

3) User supplies: "22-13"

Probably should behave like #2 above...but I'm open to other clean ways to validate the input.

I have worked on the following so far, which isn't very clean, complete, or even 100% correct for what I was asking - but it shows the effort and idea I'm going for:

echo "line?"; read -r deleteline
case "$deleteline" in
        ''|*[!0-9\-]*)   echo "not a number";;
        [1-9]|[1-9][0-9]*);;
esac


deleteline_lb=$(echo $deleteline|awk -F "-" '{print $1}')
deleteline_ub=$(echo $deleteline|awk -F "-" '{print $2}')

if [ ! $deleteline_lb = "" ] && [ ! "$deleteline_ub" = "" ]; then
        delete_line_count=1
        delete_line_count=$(expr $deleteline_ub - $deleteline_lb)
        if [ $delete_line_count -le 0 ]; then
                delete_line_count=1
        fi
fi

i=1; while [ $i -le $delete_line_count ]; do
  echo $deleteline_lb $i
  i=$(($i + 1))
done

This needs to run in sh, things like seq are not supported - so stick with posix compliant methods...


To clarify I am looking to do the following (pseudo-code): 1) accept input from user 2) validate if input is in the form "#" or "#-#" (range). 3) Execute chosen (arbitrary) code path based on proper/improper input. 4) If single # is given then store that to variable to perform future operations against. 5) If range is given, store both numbers in variable to be able to perform the operation against the lower # up to the higher number. More specifically it would be "(higher #) - (lower #) + 1". So if range were 12-17 then we need to perform operation against 12, 6x. (17 - 12 + 1). IOW, 12-17 inclusive. 6) A way to easily denote if data set is range vs single number is also desired so that code path to each can be easily branched.

thanks for helping!

UPDATE:

Using my basic code I reworked it (with a bit of input from a friend), and basically came up with this:

            while true;do
                printf "\\delete lines? [e=Exit] ";read -r deleteline
                case "$deleteline" in
                    [Ee])   break;;
                    *)
                    echo "$deleteline" | egrep -q '^[[:digit:]-]*$' 

                    if [ $? -ne 0 ]; then
                       printf "\\n input is not a number.\\n"
                    else
                        delete_range_start=`echo $deleteline|awk -F "-" '{print $1}'` 
                        delete_range_end=`echo $deleteline|awk -F "-" '{print $2}'` 

                        if [ $delete_range_end -lt $delete_range_start ]; then
                            printf "\\n upper range must be higher than lower range.\\n"
                        else
                            if [ "$delete_range_end" = "" ]; then
                                delete_range_end=$delete_range_start
                            elif [ $delete_range_end -gt $lineNumbers ]; then
                                printf "\\Setting range to last entry\\n"
                            fi
                            break
                        fi  
                    fi
                    ;;
                esac

            done

            deleteline=$delete_range_start
            deleteloop=`expr $delete_range_end - $delete_range_start + 1`

            i=1
            while [ $i -le $deleteloop ]; do
                    # Insert all processing code in here
                    i=`expr $i + 1`
            done    

Upvotes: 0

Views: 172

Answers (1)

oliv
oliv

Reputation: 13259

If you have a posix compliant awk, try this:

echo "$userInput" | awk -F- '
  ($1+0==$1&&$2+0==$2){
     for(i=$1;($1<$2?i<=$2:i>=$2);)
       print ($1<$2?i++:i--);
     next
  }
  $1+0==$1{
    print $1;
    next
  }
  $2+0==$2{
    print $2;
    next
  }
  ($1+0!=$1&&$2+0!=$2){
    exit 1
  }'

The script check if the 2 fields (separated with -) are numbers. If so, it prints these numbers in an ascending or descending way depending if the first number is greater or lower than the second one.
If only one input, the script just prints it.
If none of the field are number, it exits with a non zero value.

This script could be the validation step of a shell script like this:

$ cat test.sh
#!/bin/sh
echo -n "range: "
read -r range
validated_input=$(echo "$range" | awk -F- '($1+0==$1&&$2+0==$2){for(i=$1;($1<$2?i<=$2:i>=$2);)print ($1<$2?i++:i--);next}$1+0==$1{print $1;next}$2+0==$2{print $2;next}($1+0!=$1&&$2+0!=$2){exit 1}')
if [ $? -ne 0 ]; then
    echo "Incorrect range" >&2
fi
for i in $validated_input; do
    echo "$i"
done

Examples:

$ ./test.sh
range: 10-6
10
9
8
7
6
$ ./test.sh
range: 8-
8
$ ./test.sh
range: hello
Incorrect range

Upvotes: 1

Related Questions