The question is published on by Tutorial Guruji team.
My problem is related to music encoding on my NAS and optimisation of linux find
command.
I have write a script that is totally functionnal but I am sure this not the best way.
To summarize:
1/ Script checks for directories that contain flac to encode
- all directories containing some
.flac
files - the directories NOT containing a subdirectory named
mp3/
orMp3/
For that specific part I have done something like this :
FILE="./DirContaininfFlacToConvertToMp3.txt" find . -type f -iname '*.flac' -printf '%hn' | sort -u > fic1 find . -iname 'mp3' -type d | sort | sed 's//Mp3//' | sed 's//mp3//' > fic2 comm -3 fic1 fic2 > $FILE
- The $FILE will be filled with all the directories matching these 2 conditions.
fic1
file contains all directories containing flac files.fic2
file contains all directories containing mp3 subdir (sed is used to eliminatemp3/
orMp3/
at the end of the path for the comm command to work)comm -3
provide me my result in$FILE
It works all right but I am pretty sure this is not the best way to do and it can be optimized just using find parameters or passing it to shell ..
2/ Script Encodes the flac files and move to a Mp3/ subdir
Once the file ($FILE
) give the result of the directory that match the conditions next steps I do these:
- I encode the files in
mp3
in the defined directory - I move them to a subdirectory named
Mp3
(keeping original structure)
For that part I done that in 2 loops:
#1st loop to encode IFS=$'n' for next in `cat $FILE` do find $next -type f -name "*.flac" -exec ffmpeg -i {} -qscale:a 2 -map_metadata 0 -id3v2_version 3 {}.mp3 ; done #2nd loop for moving to subdir for next in `cat $FILE` do find $next -type f -name '*.flac.mp3' -execdir mkdir -p Mp3 ; -execdir mv {} Mp3/ ; done
Also here I am pretty sure I could avoid iterating in 2 loops.
However you could find for any purpose the full script here (not very well coded I know … 🙁
Full script
#!/bin/bash # Convert .flac en .mp3 file for Synology with ffmpeg # usage : 1st construct list of directory to convert : run: ./script.sh V # 2nd verify and to convert run : nohup ./script C & #result of list of directories to encode FILE="./ToConvertToMp3.txt" # parameter validation if [ "$#" -ne "1" ] then echo "1 parameter required : ./script.sh (V)erify (C)onvert " exit 1; fi # generate list of directory for encoding if [ "$1" == "V" ] then echo "-------- VERIFY AND CONSTRUCT LIST -------" #1 find dir that contains .flac file find . -type f -iname '*.flac' -printf '%hn' | sort -u > fic1 # for those directory search if there is some *mp3* subdir IFS=$'n' for dirflac in `cat fic1` do find $dirflac -iname '*mp3*' -type d -exec dirname {} ; >> fic2 done # sort and unicity cat fic2 | sort -u > fic3 # final construc of the list of directory to encode comm -3 fic1 fic2 > $FILE rm -f fic1 fic2 fic3 echo "-----DIRECTORY TO CONVERT-------" cat $FILE echo "-------------------------------------" exit 0 fi # Converting .flav files from directories listed in $FILE if [ "$1" == "C" ] then echo "-------- CONVERTING FLAC -------" if [ -e $FILE ]; then echo "$FILE exist : OK" else echo "$FILE does not exist Please (V)erification first" exit 3 fi if [ -s "$FILE" ]; then echo "$FILE contains directories : OK" cat $FILE else echo "$FILE is empty , nothing to do" exit 2 fi echo "-------- CONVERTING -------" IFS=$'n' for next in `cat $FILE` do find $next -type f -name "*.flac" -exec ffmpeg -i {} -qscale:a 2 -map_metadata 0 -id3v2_version 3 {}.mp3 ; album="$(basename $directory)-Mp3"; echo " Processing $directory : creating subdirectory : $album, moving files in progress" find $directory -type f -name '*.flac.mp3' -execdir mkdir -p "$album" ; -execdir mv {} "$album/" ; done echo "-------- END OF CONVERSION -------" # renaming $FILE with timestamp ficdate=$(date +%Y%M%d-%Hh%m); mv $FILE $FILE-$ficdate.done; exit 0 fi echo "WARNING : 1 parameter : usage : ./script.sh (V)erification (C)onversion"
Answer
You could do:
find . -type f -name '*.flac' -execdir sh -c ' if [ ! -d mp3 ] && [ ! -d Mp3 ]; then for file do ffmpeg -i "$file" -qscale:a 2 -map_metadata 0 -id3v2_version 3 "${file%.*}.mp3" done fi' sh {} +
The idea being that with -execdir cmd {} +
, (with some versions of GNU find
), find
will run cmd
for all the matching file in a given directory.
I say some versions of GNU find because it used to work like that, but then was broken in some versions of find
(where you’d get one cmd
invocation per file as if you had used -execdir cmd {} ;
) and it was fixed again in a later version.
You can check if you have a correct version with:
find . -execdir echo {} +
You should get one line per directory with a correct version, or one line per file with the less correct ones.
If you have a correct version, and provided you don’t have thousands of flac files per directory, you can do both actions in one go:
find . -type f -name '*.flac' -execdir sh -c ' if [ ! -d mp3 ] && [ ! -d Mp3 ]; then mkdir Mp3 || exit for file do ffmpeg -i "$file" -qscale:a 2 -map_metadata 0 -id3v2_version 3 "Mp3/${file%.*}.mp3" done fi' sh {} +