Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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 '-'.

share|improve this question
    
What have you tried? –  Flimzy Nov 3 '11 at 10:55
    
@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 Nov 3 '11 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 Nov 3 '11 at 11:43

5 Answers 5

up vote 3 down vote accepted

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
share|improve this answer
2  
I can't even read that! Perl really holds it's name as a read-only language... –  sinni800 Nov 3 '11 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 Nov 3 '11 at 12:10
    
This works most reliable for my embedded system. I'll use it. Thank you :) –  srnka Nov 4 '11 at 9:31
    
Ah, so you can use perl after all? Nice :) –  Flimzy Nov 4 '11 at 9:33

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

share|improve this answer
    
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 Nov 3 '11 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? –  Kusalananda Nov 3 '11 at 11:48
    
@srnka, did you re-type my code and forget the next? Copy and paste instead. –  Kusalananda Nov 3 '11 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 Nov 3 '11 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 Nov 4 '11 at 9:30

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[*]}

share|improve this answer
    
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 Nov 4 '11 at 7:22
    
that's because "numbers" is actually an array, as you requested. Use ${numbers[@]} to print the entire contents of the array. –  glenn jackman Nov 4 '11 at 11:09
    
Yes, it is ok then, thank you –  srnka Nov 4 '11 at 11:52

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
share|improve this answer
    
I've edited my answer –  Eir Nym Nov 4 '11 at 12:53

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.