The other answers cover your real problem. But one thing worth saying more about is how $*
and $@
differ in expansion when double quoted. tl;dr You almost always want "$@"
.
"$*"
expands to a single string containing all the parameters with the first character of IFS
in between them. In practice this usually means all params as one string separated by spaces. So if your params are file1.txt
, file2.txt
and file3.txt
, then "$*"
hands that to the loop as one thing, namely file1.txt file2.txt file3.txt
. That one thing, of course, does not exist as such.
"$@"
expands to each of the parameters, one at a time, treated as a single "word". This is good because it will treat file1.txt
, file2.txt
and file3.txt
from the earlier example correctly as three items. The loop will receive them correctly one at a time. In addition, "$@"
is good since even if your filenames have spaces in them, you will get the right behavior since the loop will handle, e.g. filename 1
, filename 2
, filename 2 and a half
as three items, one by one. (Normally the shell treats a space as marking the end of a word, so filename 1
would look like two items to the shell, filename
and 1
.
When they aren't quoted both $@
and $*
expand the words of all the positional parameters. So in that case, they are effectively identical (and both a bad idea since they don't protect you from oddly named files).
There's a good visualization of all possibilities here: http://wiki.bash-hackers.org/scripting/posparams#mass_usage.