Trying to do a mass convert from M4A to OGG in a large music collection, I have:

for i in `find /home/family/Music -name *.m4a -print0`
   #do ffmpeg -i "$i" -acodec libvorbis -aq 6 -vn -ac 2 "$i.ogg";
   do echo $i

All of the files will have spaces in their names, the output for the above shows a single file like this:


Every space marks a new line, I thought -print0 would fix this?

  • cd /home/family/Music , for in *.m4a; do ffmpeg .... ; done – Panther Oct 1 '14 at 17:22
  • $ for i in *.m4a; do echo $i; done *.m4a The files are in subfolders, don't think this works – wjdp Oct 1 '14 at 17:26
  • You should probably include such information in your question. – Panther Oct 1 '14 at 17:31
  • See superuser.com/questions/409818/… – Panther Oct 1 '14 at 17:32
  • @will Should probably be mentioned on this site to. Do not cross-post questions. Again SO has a terrific answer on this – user234837 Oct 6 '14 at 9:21

That's one of the reasons why you never use a for loop to iterate over a command whose output can contain spaces. Especially if that output is a list of file names that can contain anything except / and \0. You have fallen into bash pitfall number 1. Always use while instead. To make sure it works with all file names, including those with spaces, newlines, tabs, backslashes or any other strange characters, use this:

find /home/family/Music -name '*.m4a' -print0 | while IFS= read -r -d '' file; do
     ffmpeg -i "$file" -acodec libvorbis -aq 6 -vn -ac 2 "${file%.m4a}.ogg";


  • Note that I quoted *.mp4a which ensures that bash will not expand it before passing it to find. This is important for the cases where you have files that match that glob in your current directory.

  • The -print0, as you probably know, causes find to separate its results with \0 instead of newlines.

  • IFS= : This sets the input field character to nothing, ensuring that no word splitting will take place.

  • while read -r -d '' file: This will iterate over the results, saving each as $file, just like for file in $(command). The options are (from help read):

     -r     do not allow backslashes to escape any characters
     -d delim   continue until the first character of DELIM is read, rather
        than newline

    Setting the delimiter to the empty string (-d '') makes read play nice with find's -print0.

  • "${file%.mp3}.ogg"; : This is simply to remove the .m4a suffix and replace it with .ogg so you get foo.ogg instead of foo.m4a.ogg.

The rest is the same as you had attempted so I'm guessing you understand it.

| improve this answer | |
  • great answer , just wondering why there are 2 times do - am I missing something or is it a bug ?! ... – Yordan Georgiev Oct 1 '14 at 18:10
  • @YordanGeorgiev a bug, I had copied the command from the OP and it included a do. Thanks for spotting it. – terdon Oct 1 '14 at 18:19

Use xargs with -0 option, or use find's own exec option:

find /home/family/Music -name '*.m4a' -exec ffmpeg -i "{}" -acodec libvorbis -aq 6 -vn -ac 2 "{}.ogg" \;
# or:
find /home/family/Music -name '*.m4a' -print0 | xargs -0 -i ffmpeg -i {} -acodec libvorbis -aq 6 -vn -ac 2 {}.ogg

Note that in both cases (and in your original command), x.m4a will be converted to x.m4a.ogg.

| improve this answer | |
  • find /home/family/Music -name '*.m4a' -exec sh -c 'ffmpeg -i "$0" -acodec libvorbis -aq 6 -vn -ac 2 "${0%.m4a}.ogg"' '{}' \; will fix the filename issue. – evilsoup Oct 1 '14 at 19:14

This may be solution of what you want

find  /home/family/Music -type f -name '*.m4a' -print0 | while IFS= read -r -d '' i; 
   #do ffmpeg -i "$i" -acodec libvorbis -aq 6 -vn -ac 2 "$i.ogg"; 
   echo $i
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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