0

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?

3 Answers 3

3

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
Sign up to request clarification or add additional context in comments.

5 Comments

But why does it work fine at the first use $i and not at the second? Does the first . make any difference? In my experience it is not required to use {} when iterating in a for loop like this (when using the variable once).
@Krøllebølle: . cannot be a part of a variable name. "b" can.
Yeah, that makes sense. for i in {123,456}; do cp $ifoo.$ibar out; done fails in the same manner. I guess I have been lucky so far. Thanks!
Quoting anything that is not a variable also works. $i.foo.$i"bar"
@Jidder: Or quoting the variable $i.foo."$i"bar :-)
1

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.

Comments

1

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.

4 Comments

Which one do you refer to exactly? If you mean the first surely it won't, yet.
cp "$i.foo.$ibar" out
Of course. I haven't mentioned anything about adjacent variable characters there yet. Refer to the second one which is the true answer.
Ahh, okay , it was just a bit misleading.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.