1

I am writing a script which accepts two arguments:

#! /bin/bash

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

I run it like:

$ ./myscript 0002 0010
 syntax error near unexpected token `do'

Why is the error?

I though it might be because the looping should be grouped. But by replacing eval for i in {$1..$2}; do echo $i; done with eval { for i in {$1..$2}; do echo $i; done; }, the error remains.

Note: I hope to perform parameter expansion before brace expansion by using eval. The desired output of my example is 0002 0003 0004 0005 0006 0007 0008 0009 0010. (See Perform parameter expansion before brace expansion?)

2 Answers 2

7

That's because the shell evaluated ;, so eval didn't see it.

You have to escape any shell special character to delay its evaluation and pass is literally to eval:

eval for i in \{"$1".."$2"\}\; do echo \"\$i\"\;  done
3
  • 2
    This works; so +1. However, I think the right answer is "don't do this": seq can accomplish the same thing without the security issues of eval which, in this case can be harmlessly demonstrated by running the script with two carefully chosen arguments like ./myscript.sh 02 '0010}; do echo "Whats for lunch?"; done; touch newfile; for i in {1..2'
    – John1024
    Apr 23, 2016 at 2:19
  • 1
    @John1024: That's right. But I guess the OP seems to have the knowledge about that problem, by observing his questions before, so, I mainly focus on the why. Please feel free to make any edit.
    – cuonglm
    Apr 23, 2016 at 2:40
  • 1
    @John1024 seq isn't the right solution here either. The right thing is to use a numerical loop: for ((i=$1; i<=$2; i++)) in bash or i=$1; while [ "$i" -e "$2" ] in sh. Apr 23, 2016 at 14:31
2

That line breaks at the first metacharacter, which is ; (in this example).
The line:

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

Is divided into this three commands:

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

Trying to execute a do without having a previous for, while, until can not work. It will raise an error condition:

syntax error near unexpected token `do'

That happens because the previous for is hidden as an argument in the command eval.

One possible workaround is to quote everything and leave out only what you want to be expanded, which I believe you want the variables $1 and $2:

eval 'for i in {'$1'..'$2'}; do echo $i;  done'

It does work:

$ ./myscript 0002 0010
0002
0003
0004
0005
0006
0007
0008
0009
0010

However, this script is evaluating external input, a highly insecure practice.

Correct Solution

A safer script which sanitize $1 and $2 and properly quote the vars, is this:

#!/bin/bash
a=${1//[^0-9]/}          ### select only numbers from first parameter.
b=${2//[^0-9]/}          ### select only numbers from second parameter.
eval  'for i in {'"$a"'..'"$b"'}; do echo $i;  done'

You must log in to answer this question.

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