Krøllebølle
Krøllebølle

Reputation: 3018

Using bash for loop variable several times in the same statement

I ran into a problem I find rather strange and I hope some can explain it. I set up a simple bash for loop that iterates a few numbers and copies files that has each number two times in the file name. This fails when not using ${var_name} but succeeds when doing so. Here is a short example explaining what I mean:

$ touch 123.foo.123bar
$ touch 456.foo.456bar
$ mkdir out
$ for i in {123,456}; do cp $i.foo.$ibar out; done
cp: cannot stat `123.foo.': No such file or directory
cp: cannot stat `456.foo.': No such file or directory
$ for i in {123,456}; do cp ${i}.foo.${i}bar out; done
$ ls out
123.foo.123bar  456.foo.456bar

What is the reason that the first foor lopp fails while the second does not?

Upvotes: 0

Views: 183

Answers (3)

konsolebox
konsolebox

Reputation: 75458

It is always a good practice to quote your arguments with variables on it around doublequotes ("") in a normal command to prevent unexpected word splitting and pathname expansion:

cp "$i.foo.$ibar" out

And if you find variables getting themselves adjacent to valid variable characters, the preferred solution would always be to use the ${} format to keep it isolated. Quoting (like cp "$i.foo.$i\bar" out) may give confusion depending on implementation:

cp "${i}.foo.${i}bar" out

Using ${} most of the time is not bad as well.

Upvotes: 1

choroba
choroba

Reputation: 241768

If you write $ibar, bash understands that as ${ibar}. Indeed, where should it end the variable name? (A dot is not allowed in a variable name, so $i. doesn't cause a problem.)

Another way to prevent the problem is to use backslashes:

cp $i.foo.$i\bar out

Upvotes: 3

Costi Ciudatu
Costi Ciudatu

Reputation: 38195

Your first example references the variable $ibar, which is not defined. The second one explicitly uses $i, which is what you want.

The variable name is expanded by bash until the first word separator, therefore $i.foo will be parsed as ${i}.foo, since . terminates the word.

However, $ibar only ends at the whitespace and bash will therefore look for ${ibar} in the scope.

Upvotes: 1

Related Questions