1

I have a script to reorganize my directory structure that contains:

files=./*"$pattern"I{"$ifirst".."$ilast"}.ext
cp ${files} ../"$pattern"/"$i"/

When I run the script, I get an error:

./*S6I{001..129}.ext: No such file or directory

However, if I echo the command and then copy/paste it into the terminal I get the behavior I'd expect:

cp ./*S6I{001..129}.ext ../S6/1/

Why would this not run correctly from the script?

marked as duplicate by Kusalananda bash May 14 at 17:56

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.

  • I do start with #!/bin/bash and run with bash script.sh.Had to add eval to get it going. I had to fix an issue in getting it to respect leading zeros, but got it sorted. Thank you! – Chris May 14 at 17:29
2

Your script doesn't work the way you expect it because of the order of expansion.

From the bash manual:

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.

As you see, brace expansion is first, variable expansion is later. In an assignment, brace expansion is not performed. Otherwise an expression x={1,2} would be expanded to x=1 x=2, and that also wouldn't make sense.

One solution for this, as you found out, is eval, because the whole expansion is done twice, so the first variable expansion is performed before the second brace expansion.

Be sure you understand the risks of eval, in particular never use it for untrusted input.

As you are using bash, you can use an array.

files=( ./*"$pattern"I{"$ifirst".."$ilast"}.ext )
cp "${files[@]}" ../"$pattern"/"$i"/

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