0

This question already has an answer here:

Bash manual says that brace expansion is performed before any other expansions.

I am writing a script which accepts two arguments:

#! /bin/bash

for b in {$1..$2}; do echo $b; done

I run it like:

$ ./myscript 0002 0010
{0002..0010}

The output isn't what I hope. I hope to perform parameter expansion before brace expansion. The expected output of my example is 0002 0003 0004 0005 0006 0007 0008 0009 0010, not 2 3 4 5 6 7 8 9 10. What would you replace {$1..$2} with?

I hope that the solution works even when $1 and $2 are strings not just made of digits, but also made of letters and digits.

Note that the values $1 and $2 can only be given as arguments to the script. I think it is clear from the beginning, but point it out in case not.

marked as duplicate by Gilles bash Apr 22 '16 at 12:12

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • seq $1 $2? eval? – Mikel Apr 22 '16 at 3:37
0

Brace Expansion is not defined by POSIX, so perhaps you could ditch it altogether:

seq $*

Or:

j=$1
while [ $j -le $2 ]
do
  echo $j
  j=$((j+1))
done

Or:

echo $* | awk '{for (j = $1; j <= $2; j++) print j}'
  • Thanks. What if $1 and $2 are not numbers, but strings made of letters and digits? What would you replace {$1..$2} with? For example the expected output of my example is 0002 0003 0004 0005 0006 0007 0008 0009 0010, not 2 3 4 5 6 7 8 9 10. – Tim Apr 22 '16 at 3:51
  • printf on each value returned by seq – Mikel Apr 22 '16 at 4:01
  • Also consider when $1 and $2 are made of letters. – Tim Apr 22 '16 at 4:03
  • @StevenPenny: You might want to get into the habit of using "$@" instead of $*. – G-Man Apr 22 '16 at 6:42
  • @G-Man: why the habit? – Tim Apr 22 '16 at 13:16
0
#! /bin/bash

seq -w $1 $2

This may be what you find!

0

To do what you ask for: perform parameter expansion before brace expansion.

We need to delay brace expansion, that is easily done by quoting it \{...\} and calling eval:

$ set -- 5 10
$ eval printf \'%s \' \{$1..$2\}
5 6 7 8 9 10

That would work fine as long as there are no commands inside $1 or $2.
That is a security risk of this solution. One way to mitigate the issue is to make sure that the variables only contain numbers:

#!/bin/bash

a=${1//[^0-9]/}          ### select only numbers from first parameter.
b=${2//[^0-9]/}          ### select only numbers from second parameter.

c=$(eval printf \'%s \' \{$a..$b\})

for i in $c; do echo "$i"; done

The above code is missing some quoting that this version corrects:

#!/bin/bash
a=${1//[^0-9]/}          ### select only numbers from first parameter.
b=${2//[^0-9]/}          ### select only numbers from second parameter.
c=( $(eval printf \'%s \' \{$a..$b\}) )

for i in "${c[@]}"; do echo "$i"; done

### Or just
# printf '%s\n' "${c[@]}"

But to print a list of numbers, an arithmetic for loop looks as a better solution:

#!/bin/bash

a=${1//[^0-9]/}          ### select only numbers from first parameter.
b=${2//[^0-9]/}          ### select only numbers from second parameter.

for (( i=$a; i<=$b; i++)); do
    printf '%0*d\n' 5 "$i"
done    

Use it as:

$ script.sh 5 15
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015

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