How can I use $variable in a shell brace expansion of a sequence?

You may want to try :

eval rm foo.{$ext0..$extN}

Not sure whether this is the best answer, but it certainly is one.


As you already realized, {1..3} expands to 1 2 3 but {foo..bar} or {$foo..$bar} don't trigger brace expansion, and the latter is subsequently expanded to replace $foo and $bar by their values.

A fallback on GNU (e.g. non-embedded Linux) is the seq command.

for x in `seq $ext0 $extN`; do rm foo.$x; done

Another possibility. if foo. contains no shell special character, is

rm `seq $ext0 $extN | sed 's/^/foo./'`

The simplest solution is to use zsh, where rm foo.{$ext0..$extN} does what you want.


While the other answers discuss using eval and seq, in bash, you can use a traditional C style for loop in an arithmetic context. The variables ext0 and extN are expanded inside the ((..)) causing the loop to run for the range defined.

for (( idx = ext0; idx <= extN; idx++ )); do
    [[ -f "$foo.$idx" ]] || { printf "file %s does not exist" "$foo.$idx" >&2 ; continue ; }
    rm "$foo.$idx"
done

If you are looking for an optimal way and avoid multiple rm commands, you can use a temporary placeholder to store the filename results and call rm in one-shot.

 results=()
 for (( idx = ext0; idx <= extN; idx++ )); do
     [[ -f "$foo.$idx" ]] || { printf "file %s does not exist" "$foo.$idx" >&2 ; continue ; }
     results+=( "$foo.$idx" )
 done

and now call the rm command on the expanded array

 rm -- "${results[@]}"