user2840265 user2840265 - 18 days ago 4
Bash Question

Bash syntax weirdness with files with spaces

Here's a problem you may be familiar with: I wanted to make an inquiry into my disk usage on my Mac, so I tried in my home directory

~/$> for file in `ls`; do du -hs $file; done


with output

55M Applications
107M Desktop
132G Documents
5.1G Downloads
16G Library
457M Music
53M Pictures
du: VirtualBox: No such file or directory
du: VMs: No such file or directory


which is great except for the last one because it is a directory called "VirtualBox VMs" -- there is a space in the name. I google it (actually, all I had to do was start typing "bash loop..." and it autocompleted to "bash loop over files with spaces" -- thanks Google!) and I get this question, Iterate over list of files with spaces, as the first result.

If you peruse that thread, you'll find a lot of complicated solutions that use
xargs
or something weird. Of course, I could write my own functions to handle these cases, but I was pretty adamant about having a one line solution. Thus, in my opinion, the hands-down best solution (though not the top-voted or accepted solution) was

for file in *; do du -hs "${file}"; done


Somewhat perversely, the following don't work:


  1. Removing the quotes

    for file in *; do du -hs ${file}; done #splits on spaces and throws error

  2. Using
    ls
    , even with the quotes:

    for file in `ls`; do du -hs "${file}"; done #splits on spaces and throws error



My question is, Can some bash expert explain to me exactly what the difference between all these is, and why the ones that fail fail?

jxh jxh
Answer

Realize that the working case expands to: du -hs "VirtualBox VMs".

  1. For this case, the issue is that the command expands to:
    du -hs VirtualBox VMs
    This passes two file names to the du command.

  2. For this case, the issue is that the for command sees two separate tokens. So the iteration iterates over each separately.
    du -hs VirtualBox
    du -hs VMs