2

In my music directory I have multiple duplicate files of both .mp3 and .flac format. Consider:

/dir1/music1.flac
/dir1/music1.mp3
/dir2/music1.mp3
/dir2/music2.MP3
/dir2/music2.flac
/dir2/music3.mp3

music1.flac and music1.mp3 in dir1 are the same song in different formats, but music1.mp3 in dir1 and dir2 may not be (same name, but released on different albums).

I wish to traverse multiple subdirectories finding files with an identical prefix but different extensions, and then delete just the mp3 files. As such, for the above directories, I'd be left with:

/dir1/music1.flac
/dir2/music1.mp3
/dir2/music2.flac
/dir2/music3.mp3

I've tried to use the find command with the logical AND, but to no avail. Some failures on my part:

find ./ -regex '.*\(mp3\|flac\)$'
find . -type f -name "*mp3" -and -name "*flac"

Any help is appreciated. I've solved similar problems with posted stackoverflow code on my own, but I'm stumped on this one. YOU GUYS ARE GREAT.

doogie
  • 23
  • 3

2 Answers2

3

Try this (Shellcheck-clean) code:

#! /bin/bash

shopt -s nullglob   # Globs that match nothing expand to nothing
shopt -s globstar   # ** matches multiple directory levels

for mp3_path in **/*.[Mm][Pp]3 ; do
    # Find '.flac', '.FLAC', '.Flac', ..., files with the same base
    flac_paths=( "${mp3_path%.*}".[Ff][Ll][Aa][Cc] )
    if (( ${#flac_paths[*]} > 0 )) ; then
        # There is a(t least one) FLAC file so remove the MP3 file
        printf "Removing '%s'\\n" "$mp3_path"
        # rm -- "$mp3_path"
    fi
done

It requires Bash version 4.0 or later because it uses globstar.

Since your example included both lower case and upper case '.mp3', the code handles any case of '.mp3' or '.flac'. It can be simplified if that is not necessary.

Remove the comment on the rm line to make it actually remove files.

pjh
  • 6,388
  • 2
  • 16
  • 17
0

This will confirm the files that will be deleted:

find . -type f|sort|awk -F ":" '{match($1,"(.+)\\.[A-Za-z0-9]+$",base); if (base[1]==prev) {fordel=$1} else {fordel=null};if (base[1]) prev=base[1]; if(fordel) {print "\"" fordel "\""}}'|xargs echo

And this will handle the deletion:

find . -type f|sort|awk -F ":" '{match($1,"(.+)\\.[A-Za-z0-9]+$",base); if (base[1]==prev) {fordel=$1} else {fordel=null};if (base[1]) prev=base[1]; if(fordel) {print "\"" fordel "\""}}'|xargs rm

Both solutions will handle spaces in files.

Borislav Markov
  • 1,495
  • 11
  • 12
  • Thanks for your contribution. This code works on files without any spaces but fails to delete pairs of files such as "Christmas Song.mp3" and "Christmas Song.flac" – doogie Jan 11 '19 at 18:28