Trying to answer the question Are true dynamic folders (NOT a “Smart Folder” SavedSearch) possible? I stumbled about the problem to loop the results of an mdfind search as quoted or escaped paths.

Code snippet:

for File in $(mdfind -onlyin $MusicSamples 'kMDItemAudioBitRate >= "44000"  && _kMDItemUserTags = "Sample"')
    ln -s $File $DrumFoldr

The for loop should create soft links of all matching files in the folder $MusicSamples or its sub folders in the folder $DrumFoldr. The loop works for files with paths/file names without spaces.

  • The answer to a similar question simply suggests to quote $File (... "$File" ...). This doesn't work - it simply creates broken soft links with names of the contiguous strings in the original file name: a file named "1. Artist - Song - Mix.mp3" will create four or five soft links: "1.", "Artist", "Song", "Mix.mp3" and "-".

  • Escaping the spaces by piping the mdfind results to a sed command replacing a space with an escaped space will result in something like "\ 1.", "\ Artist" etc.

  • Creating an array of the mdfind results with:

    mdfind ... | while IFS= read -r filename; do

    and using it in for File in "${result[@]}"; do ln -s "$File" $DrumFoldr; done doesn't work either.

How do I properly escape those spaces in the paths?

  • 1
    mdfind has a null option. You could then try piping the results through xargs.
    – fd0
    Apr 17, 2017 at 19:47
  • @fd0 I tried this also (haven't mentioned it in my question though) - without success. But I may have made a mistake in the command.
    – klanomath
    Apr 17, 2017 at 20:00

3 Answers 3


The best way to handle filepaths with funny characters is to use null delimiters (as fd0 suggested in a comment). mdfind -0 will use this output format, very much like find ... -print0. You can then parse & use the results with xargs -0:

mdfind -0 -onlyin "$MusicSamples" 'kMDItemAudioBitRate >= "44000"  && _kMDItemUserTags = "Sample"' |
    xargs -0 -J % ln -s % "$DrumFoldr"

(Note that I also double-quoted $MusicSamples and $DrumFoldr, to prevent trouble if either of those contain spaces. Double-quoting variable references is almost always a good idea. Also, this xargs invocation will pass many filepaths with each run of ln -- if this is a problem, you may need to add something like -n1 to make it pass them one-by-one.)

If you need to do something more complicated than xargs can handle easily, you can also use a shell while read loop with some special options:

mdfind -0 -onlyin "$MusicSamples" 'kMDItemAudioBitRate >= "44000"  && _kMDItemUserTags = "Sample"' |
    while IFS= read -r -d '' file; do
        ln -s "$file" "$DrumFoldr"

(The special options used with read are: -d '' to make it use nulls as delimiters, -r to tell it not to do weird things with backslashes, and IFS= to prevent it from trimming whitespace from the beginning or end of the lines.)

See BashFAQ #20: "How can I find and safely handle file names containing newlines, spaces or both?" for more info and options.


I just solved a similar problem as I was trying to iterate over mdfind's results of an image search.

mdfind image -onlyin $1 | while read line; do 
    ((counter = counter + 1))
    echo "$counter: $line"

So adapting that to your problem, this should work:

mdfind -onlyin $MusicSamples 'kMDItemAudioBitRate >= "44000" && _kMDItemUserTags = "Sample"' | while read File
    ln -s "$File" "$DrumFoldr"

I can tell you I recently found out why the following does not work for me:

    mdfind ... | while IFS= read -r filename; do

According some research on other sites: https://stackoverflow.com/questions/16854280/a-variable-modified-inside-a-while-loop-is-not-remembered

So following the CSV example in that answer I can confirm the following works for me:

    while read -r line; do
    done <<< "$(mdfind "kMDItemAppStoreCategory == 'Productivity'")"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .