Search code examples
bashavconv

Convert multiple recursive video files using "find" and "avconv"


For converting MOV files to mp4 in the directory, I was using (I use more commands with avconv, but I shortened for syntetize):

for f in *.MOV; do echo "Converting $f"; avconv -i "$f" "${f:0: -4}.mp4"; done

And it works. It converts every file.

But now, I want to convert all the files in the directory and all subdirs (recursivelly). I tried:

for f in "$(find ./ -name '*.MOV')"; do echo "Converting $f"; avconv -i "$f" "${f:0: -4}.mp4"; done

But it doesn't work, because it outputs:

mario@circo3d:~/Imágenes$ for f in "$(find ./ -name '*.MOV')"; do echo "Converting $f"; avconv -i "$f" "${f:0: -4}.mp4"; done
Converting ./2015-05-23 Tutorial Masa de colores/MVI_9219.MOV
./2015-05-23 Tutorial Masa de colores/MVI_9196.MOV
./2015-05-23 Tutorial Masa de colores/MVI_9199.MOV
./2015-05-23 Tutorial Masa de colores/MVI_9200.MOV
avconv version 9.18-6:9.18-0ubuntu0.14.04.1, Copyright (c) 2000-2014 the Libav developers  built on Mar 16 2015 13:19:10 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
./2015-05-23 Tutorial Masa de colores/MVI_9219.MOV
./2015-05-23 Tutorial Masa de colores/MVI_9196.MOV
./2015-05-23 Tutorial Masa de colores/MVI_9199.MOV
mario@circo3d:~/Imágenes$ 

(Last file list appear in red)

It seems that find works, it enters in every dir and echo "Converting $f" too... but avconv receives all that filenames as a list with newlines, not each one element from the "for" loop.

Why echo works and avconv doesn't?

Or...

Why for f in *.MOV' works with avconv and for f in "$(find ./ -name '*.MOV') doesn't?


Solution

  • This is because you put them in quotes. In POSIX a newline character can very well be present in the name of the file.

    The easiest solution for you would be to rewrite using -exec attribute of find:

    find . -name "*.MTS" -exec echo {}  \; -exec avconv -i {} {}.mp4 \;
    

    Or even better, you could use -execdir for the avconv line, it will execute the command from the directory where the file is found.


    From your comment i see that you are having difficulty seeing where the newlines are coming from at all. So

    From man page of find:

    If no expression is given, the  expression  -print  is  used
    

    and

    -print True; print the full file name on the standard output,  followed
           by  a  newline.
    

    So find actually prints all the newline characters for you. You are calling it via $(find ...) and then you place it in quotes, which means that all the newline characters are preserved as a regular character.

    This is why your for loop executes only once.

    If you absolutely must use a loop, rather than using find's own execution, you probably want to use a while loop:

    find . -name "*.MTS" | while read f; do echo "Converting $f"; avconv -i "$f" "${f:0: -4}.mp4"; done