1

I'm trying to find a script that would basically look recusively at a directory, and if the script finds file that are .flac files, it will create a subfolder called FLAC in that same folder, and move the .flac files only into that directory. It may find 30 flac files in the same directory, so I don't want it to meltdown once it realizes the folder already exists, etc...

Example folder/file layout:

  • Base path is

    /files/music
    
  • Subdirectories currently look like

    /files/music/artist
    /files/music/artist/album1
    /files/music/artist/album2
    
  • Files appear as

    /files/music/artist/album1/01-song 1.mp3
    /files/music/artist/album1/01-song 1.flac
    /files/music/artist/album1/02-another song.mp3
    /files/music/artist/album1/02-another song.flac
    /files/music/artist/album2/01-yet another.mp3
    /files/music/artist/album2/01-yet another.flac
    
  • So essentially I want it to become:

    /files/music/artist/album1/01-song1.mp3
    /files/music/artist/album1/02-another song.mp3
    /files/music/artist/album1/flac/01-song 1.flac
    /files/music/artist/album1/flac/02-another song.flac
    /files/music/artist/album2/01-yet another.mp3
    /files/music/artist/album2/flac/01-yet another.flac
    

The whole idea, is that after scanning in a ton of my CDs, I have a lot of folders with mixed versions...so media players end up playing the song song twice (first the mp3 version, then the flac version)...

Is it possible to script something that would leave the directory as-is, if no mp3 files exist? (only flac?) Thus, if a folder had solely .flac files, it wouldn't create a subfolder...leave it as-is. The only hiccup I would see would be that it would have to look for mp3 files, as there might be other files in the folders (jpg cover files, etc..)

  • DayOff - that's what I'd use (assuming you're in files): find . -type d -exec sh -c 'ls "$0"/*.mp3 >/dev/null 2>&1 && ls "$0"/*.flac >/dev/null 2>&1 && mkdir "${0}/FLAC" && mv "${0}"/*.flac "${0}/FLAC"' {} \; - it's not very efficient but it should work with all kinds of file names / dir names. Add echo before mkdir and before mv to see what it does without actually running those commands. – don_crissti May 30 '15 at 22:01
  • Only hiccup I noticed is that it didn't handle variations of .FLAC (Case sensitivity) – DayOff May 31 '15 at 1:09
  • Yeah... to handle those too just run (before that command) shopt -s nocaseglob if you're using bash or unsetopt CASE_GLOB if you're using zsh. – don_crissti May 31 '15 at 9:06
2

An easy version that always creates flac and mp3 subdirectories (unless they would be empty), using the find command to execute a script to create a subdirectory if necessary and move the file there:

find . -name '*.mp3' -o -name '*.flac' -exec sh -c 'mkdir -p "${0%/*}/${0##*.}" && mv "$0" "${0%/*}/${0##*.}"' {} \;

For each file for which the shell snippet is executed, $0 is the path to the file, ${0%/*} is the directory part and ${0##*.} is the extension.

Alternatively, in bash (or ksh93 with set -o globstar instead of shopt -s globstar extglob, or in zsh without setopt ksh_glob instead), using the ** pattern:

shopt -s globstar extglob
for x in **/*.@(mp3|flac); do
  mkdir -p "${x%/*}/${x##*.}" && mv "$x" "${x%/*}/${x##*.}"
done

Now let's make a version that doesn't create a subdirectory if all the files have the same extension. Here it's easier to recurse on the directories. This is for bash (it can be adapted to ksh93 or zsh). In each directory, the script collects the list of all files (excluding . and ..), all.flacfiles and all.mp3files in arrays. If there is at least one flac file and at least one non-flac files, move the flac files to aflac` subdirectory. Ditto for mp3 files.

shopt -s globstar nullglob; GLOBIGNORE=.:..
start_wd=$PWD
for dir in "$PWD"/**/*/; do
  cd "$dir"
  files=(*)
  flac_files=(*.flac)
  mp3_files=(*.mp3)
  if ((${#flac_files[@]} > 0 && ${#flac_files[@]} < ${#files[@]})); then
    mkdir flac && mv "${flac_files[@]}" flac/
  fi
  if ((${#mp3_files[@]} > 0 && ${#mp3_files[@]} < ${#files[@]})); then
    mkdir mp3 && mv "${mp3_files[@]}" mp3/
  fi
done
cd "$start_wd"
2

Use this:

#!/bin/bash

find /files/music/artist -type f -name '*.flac' | while IFS= read -r file; do
    dir="$(dirname "$file")"
    file="$(basename "$file")"
    shopt -s nullglob
    mp3s=( "$dir"/*.mp3 )

    if ! [[ ${#mp3s[@]} -eq 0 ]]; then
        ext="${file##*.}"
        mkdir -p "$dir/$ext" && mv -i "$dir/$file" "$dir/$ext"
    fi
done
  • find will find files having .flac extension in the mentioned directory and all its subdirectories.

  • dir will contain the directory names of the files and file will have the file name

  • Then we have checked if that directory has any .mp3 file, if not then the files will remain as it is in the directory

  • If there is any .mp3 file in that directory, then a directory having the name of the extension (flac) will be created and .flac files will moved to that directory.

  • This runs dirname, basename, and a glob for each FLAC file. It's rather inefficient. – lcd047 May 30 '15 at 19:28
  • @lcd047 Why do you think that dirname, basename result in inefficiency ? – heemayl May 30 '15 at 22:17
  • Why do you think they don't? They are external processes, and you run them for every single FLAC file. – lcd047 May 31 '15 at 4:16
0

You have a bit complicated requirements, but this script might help.

#!/bin/bash

run() {
        if $DRYRUN; then
                echo -n +
                printf " '%s'" "$@"
                echo
        else
                "$@"
        fi
}

DRYRUN=false
if test x"$1" = x-d; then
        DRYRUN=true
        shift
fi

for dir in "$@"; do 
        find "$dir" -type d |
        while read subdir; do
                if ls "$subdir"/*.flac >/dev/null 2>&1; then
                        if ls "$subdir"/*.mp3 >/dev/null 2>&1; then
                                run mkdir -p "$subdir"/flac
                                run mv "$subdir"/*.flac "$subdir"/flac
                        fi
                fi
        done
done

You can see what will be going on by specifying -d. Be sure double-check it before actual moves.

$ ./script.sh -d /files/music
+ 'mkdir' '-p' '/files/music/artist/album2/flac'
+ 'mv' '/files/music/artist/album2/01-yet another.flac' '/files/music/artist/album2/flac'
+ 'mkdir' '-p' '/files/music/artist/album1/flac'
+ 'mv' '/files/music/artist/album1/01-song 1.flac' '/files/music/artist/album1/02-another song.flac' '/files/music/artist/album1/flac'

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.