Reputation: 3018
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
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
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
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