Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

The situation is, I have an MP3 player mpg321 that accepts a list of files as argument. I keep my music in a directory named "music", in which there are a few more directories. I just want to play all of them, so I run the program with

mpg321 $(find /music -iname "*\.mp3")

. The problem is, some file names have whitespace in them, and the program breaks those names into smaller parts and complains about missing files. Wrapping the result of find in quotes

mpg321 "$(find /music -iname "*\.mp3")"

does not help because all will become one big "file name", which is obviously not found.

How can I do this then? If that matters, I am using bash, but will be switching to zsh soon.

share|improve this question
add comment

3 Answers

up vote 7 down vote accepted

Try using find's -print0 or -printf option in combination with xargs like this:

find /music -iname "*\.mp3" -print0 | xargs -0 mpg321

How this works is explained by find's manual page:

-print0

True; print the full file name on the standard output, followed by a null character (instead of the newline character that -print uses). This allows file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output. This option corresponds to the -0 option of xargs.

share|improve this answer
    
Sorry for all the editing. I seem to remember the -print0 xarg -0 pattern failing on other types of whitespace, but I couldn't find an example. –  Steven D Oct 1 '10 at 16:35
    
oh yes that works! but I still wonder why mpg321 $(find /music -iname "*\.mp3" -print0) doesn't? –  phunehehe Oct 1 '10 at 17:13
1  
Well, I believe that doesn't work for two reasons: (1) the file names are separated by null characters, which is not what mpg321 is expecting at all and (2) xargs is doing the work of escaping the other whitespace, not find. –  Steven D Oct 1 '10 at 17:18
2  
@phunehehe, @Steven: mpg321 has nothing to do with it, it's the shell that's breaking up the output of find into separate arguments. And -print0 | xargs -0 will work with every possible file name. –  Gilles Oct 1 '10 at 18:53
add comment
find /music -iname "*\.mp3" -exec mpg123 {} +

With GNU find, you can also use -print0 and xargs -0, but there's little point in learning yet another tool. The -exec ... {} + syntax gets little mention because Linux acquired it later than -print0, but there's no reason not to use it now.

With zsh or bash 4, this is a lot simpler:

mpg123 **/*.[Mm][Pp]3

In zsh only, you can make a (part of a) pattern case-insensitive:

mpg123 (#i)**/*.mp3
share|improve this answer
    
+1 to this, "find -print0" is an ugly hack. –  p-static Oct 2 '10 at 6:04
add comment

I think Steven's solution is best, but another way is to use xargs' -I flag, which lets you specify a string that will then be replaced in the command with the argument (instead of just appending the argument onto the end of the command). You can use that to quote the argument:

find /music -iname "*\.mp3" | xargs -0 -Ifoo mpg321 "foo"
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.