3

What is the best approach to convert the string containing the range of numbers into numbers itself? This format is used for example in defining the range of pages to print. I define, I want to print these pages "11-14,17,20". Now I need to write the bash script which will change this string into the array of numbers: 11, 12, 13, 14, 17, 20

It should be able to work with string which contain the numbers (even with more digits) and with ',' and '-'.

2
  • @Flimzy - I have tried to use sed on it - to devide the string into rows any time the char '-' is found. And then look at the last number of previous row and the first row of actual row and make for cycle with incrementing... A little bit complicated way :)
    – srnka
    Commented Nov 3, 2011 at 11:42
  • Yeah, sed could be made to work, with enough other supporting code. Perl is a lot more powerful, and therefore more concise (if not ugly) for this sort of thing, if you're willing to use perl, as my answer below suggests.
    – Flimzy
    Commented Nov 3, 2011 at 11:43

5 Answers 5

4

This does it in Perl:

echo 11-14,17,20 | perl -pe 's/(\d+)-(\d+)/join(",",$1..$2)/eg'

Output:

11,12,13,14,17,20
4
  • 2
    I can't even read that! Perl really holds it's name as a read-only language...
    – sinni800
    Commented Nov 3, 2011 at 11:25
  • Very nice solution, in one row :) And it works for me well! But I can't use perl - the final script should be independent from perl.
    – srnka
    Commented Nov 3, 2011 at 12:10
  • This works most reliable for my embedded system. I'll use it. Thank you :)
    – srnka
    Commented Nov 4, 2011 at 9:31
  • Ah, so you can use perl after all? Nice :)
    – Flimzy
    Commented Nov 4, 2011 at 9:33
3

Here's a solution using awk:

#!/bin/bash

pages="11-14,17,20"

echo $pages | awk 'BEGIN { RS=","; FS="-"; ORS="" }
  NR > 1  { print "," }
  NF > 1  { for (i=$1; i<$2; ++i) { print i "," } print $2; next }
          { print $1 }'

This results in the following output:

11,12,13,14,17,20

5
  • Thank you - it is just I was looking for. But how does it work? When I want to convert range 11-14, the output is: 11,12,13,14,14 The last one is doubled. And I cannot correct it.
    – srnka
    Commented Nov 3, 2011 at 11:38
  • Hmm, I can not reproduce this myself. I've tried it with GNU awk 4.0.0 and with GNU awk 3.1.5, as well as with the standard awk installations on OpenBSD and Mac OSX, and all seems to work ok. What OS and awk are you using? Commented Nov 3, 2011 at 11:48
  • @srnka, did you re-type my code and forget the next? Copy and paste instead. Commented Nov 3, 2011 at 11:49
  • Yes, there could be a problem - I use it at embedded linux, awk is a part of busybox package: BusyBox v1.18.4 (2011-06-22 18:48:08 CEST) multi-call binary.
    – srnka
    Commented Nov 3, 2011 at 11:58
  • I have a problem with range containing number 9, but it seems to be a bug in awk from busybox.. Just for info :)
    – srnka
    Commented Nov 4, 2011 at 9:30
2

Just bash:

expand_ranges() {
  local IFS=,
  set -- $1
  for range; do
    case $range in 
      *-*) for (( i=${range%-*}; i<=${range#*-}; i++ )); do echo $i; done ;;
      *)   echo $range ;;
    esac
  done
}
numbers=( $(expand_ranges 11-14,17,20) )

For your requested output: IFS=,; echo ${numbers[*]}

2
  • It doesn't work for me. It just write out the first number and finish. I use GNU bash, version 3.2.39(1)
    – srnka
    Commented Nov 4, 2011 at 7:22
  • that's because "numbers" is actually an array, as you requested. Use ${numbers[@]} to print the entire contents of the array. Commented Nov 4, 2011 at 11:09
0

If you have jot from your system

Example of using jot and sed is (don't forget about newline character in replacement string!):

$echo 1-2,3,6-9| sed 's/,/\
/g'|sed -E '/-/s/([0-9]+)-([0-9]+)/- \1 \2/g;s/^([0-9]+)$/- \1 \1/g'| xargs -L 1 jot | rs -t -C,
1,2,3,6,7,8,9

If you type echo jot instead of "jot" (and remove rs call) you should see following output:

jot - 1 2
jot - 3 3
jot - 6 9
1
  • I've edited my answer
    – Eir Nym
    Commented Nov 4, 2011 at 12:53
0

This is a lisp-like way to do things (except for the fact that bash doesn't like lazy lists, so we have to set an upper bound on the maximal page number; here I've chosen 10000)

pages="11-14,17,20"; for ((i=1;i<10000;i++)); do echo -n $i, ; done | cut -d , -f $pages

With pages you specify the pages range as in the other post. The for-loop generates a list of page numbers. Of those the cut command picks out the ones you want.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .