1

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?

Community
  • 1
  • 1

2 Answers2

1

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

jxh
  • 69,070
  • 8
  • 110
  • 193
0

This is all explained by the fact that word-splitting happens after process substitution and parameter expansion. So the result of $(ls) as well as ${file} undergoes word-splitting, while the double quotes in "${file}" suppress word-splitting.

Jens
  • 69,818
  • 15
  • 125
  • 179