This is a problem Internal Field Separator. Rather, it's not a problem, it was designed that way. It splits up the input by whitespace, just as in positional parameters.
From the Bash man page:
for name [ [ in [ word ... ] ] ; ] do list ; done
The list of words following in is expanded, generating a list of items. The variable name is set to each element of this list in turn, and list is executed each time. If the in word is
omitted, the for command executes list once for each positional parameter that is set. The return status is the exit status of the last command that executes. If the
expansion of the items following in results in an empty list, no commands are executed, and the return status is 0.
This means, that it expands the results of the expression into a list of words. This is where the Internal Field Separator, $IFS comes in. By default, $IFS is set to whitespace (space, tab, and newline). This means that a field consists of everything up to (but not including) these whitespace characters. Because the filenames consist of spaces, it is treating them as several different fields and running the loop once for each field. The way to fix this is to adjust $IFS value.
So, you need to adjust the $IFS to something other than whitespace. Using your current method however there would be no way to tell where one file name ends and the other begins unless they all happen to have file extensions. An easier way to go about this would be using the
find utility like so:
#Save the original IFS
#Set the new IFS to the ASCII null character \x00
IFS=$(echo -ne "\x00")
files="" #To prevent scoping issues
#Use the find utility to find and print just regular files (not directories),
#and not going further than ~/Downloads (remove '-maxlength 1' to make the
#search fully recursive), then print the filenames in the format
#However, since our new IFS is now \x00, the boundary upon which these filenames are
#separated now works as expected
for f in $(find ~/Downloads -maxdepth 1 -type f -print0)
I would also like to point out that your statement
ls ~/Downloads | grep -v ^d probably doesn't do what you expect. It sounds like you were trying to filter directories out, but what is actually happening here is that it's filtering out files that start with 'd'. This is because you're not using the long form of
ls -l prints output like:
drwxrwxrw- user group SomeDirectory size date_modified filename
wheras 'ls' prints just the files names: