Home > Net >  In every subfolder of source folder copy newest mp3 file to destination folder and delete old files
In every subfolder of source folder copy newest mp3 file to destination folder and delete old files

Time:01-14

I'm writing a bash script that does the following:

  1. In the source directory find all subfolders
  2. Within those subfolders find the newest mp3 file
  3. Copy this mp3 file to the destination folder
  4. If there is an old mp3 file in the destination folder, that is not the newest mp3 file in the subfolder of the source folder, delete it.

Here is a visualisation of the folder structure:

sourcefolder/
├─ subfolder 1/
│  ├─ Asound1.mp3
│  ├─ Asound2.mp3
│  ├─ Asound3.mp3
├─ subfolder 2/
│  ├─ Bsound1.mp3
│  ├─ Bsound2.mp3

Here is my code so far:

#!/bin/bash

# Define the source directory containing subfolders
src_dir="PATH_SRC_DIR"

# Define the destination directory to copy the newest mp3 file
dest_dir="PATH_DEST_DIR"

# Create an empty array
files_to_keep=()

# Find the newest mp3 file in each subfolder of the source directory
find "$src_dir" -type d -exec sh -c 'cd "{}" && newfile=$(ls -t *.mp3 | head -n 1); echo "$PWD/$newfile"' \; | while read file; do
    echo "Newest file found: $file"
    # Add the newest file to the array
    files_to_keep =("$(basename "$file")")
    # Copy the file to the destination directory
    cp "$file" "$dest_dir"
done

echo "Files to keep: ${files_to_keep[@]}"

# Delete the files in the destination directory that are not the newest files
for filename in "${files_to_keep[@]}"; do
    echo "Deleting files not named: $filename"
    find "$dest_dir" -type f ! -name "$filename" -delete
done

My script successfully copies the newest mp3 files into the destination folder, so it looks like this

destfolder/
├─ Asound3.mp3
├─ Bsound2.mp3

But if I add a file to the source folder so it looks like this:

sourcefolder/
├─ subfolder 1/
│  ├─ Asound1.mp3
│  ├─ Asound2.mp3
│  ├─ Asound3.mp3
├─ subfolder 2/
│  ├─ Bsound1.mp3
│  ├─ Bsound2.mp3
│  ├─ Bsound3.mp3  <-- new file

My script will have added the new file without deleting the old file (in this case it should delete Bsound2.mp3)

destfolder/
├─ Asound3.mp3
├─ Bsound2.mp3 <-- file should be deleted
├─ Bsound3.mp3

The problem is that it doesn't delete the old files, so I suspect there is an issue with the way I populate the array or something like that.

CodePudding user response:

The problem is it does not delete the old files.

Something like this might do what you wanted.

files_to_keep=()

while IFS= read -r directory; do
  (
  cd "$directory" || exit
  new_file=$(ls -t *.mp3 | head -n1)
  echo "$PWD/$newfile"
  printf 'Newest file found: %s\n' "$new_file"
  file_name=$(basename "$new_file")
  files_to_keep =("$file_name")
  cp "$new_file" "$dest_dir" || exit
  )
done < <(find "$src_dir" -type d)

  • The ls can be replace with something better maybe with GNU find feature.

Since I'm not particularly a fan of ParsingLS, this might be an alternative:

files_to_keep=()

while IFS= read -rd '' directory; do
  latest=
  shopt -s nullglob
  for f in "$directory"/*.mp3; do
    [[ "$f" -nt "$latest" ]] && latest="$f"
  done
  shopt -u nullglob
  [[ -n "$latest" ]] && {
    printf 'Newest file found: %s\n' "$latest"
    file_name=$(basename "$latest")
    files_to_keep =("$file_name")
    cp -v "$latest" "$dest_dir" || exit
  } 
done < <(find "$src_dir" -type d -print0)

  • GNU cp(1) has the -u flag/option, for whats its worth.

  • There might be a few bugs from the scripts above, I haven't really tested it thoroughly though.


for i in "${!files_to_keep[@]}"; do
  files =(-o -name "${files_to_keep[$i]}")
done

find "$dest_dir" -type f ! \( "${files[@]:1}" \) -exec echo {}  

  • Replace the -exec echo {} with -delete or -exec rm -rf {} If you're satisfied with the output.

A small snippet about the array files above.

files_to_keep=({1..5}.mp3)

for i in "${!files_to_keep[@]}"; do
  files =(-o -name "${files_to_keep[i]}")
done

##: Note: Just for the human eyes to see DO NOT
##: use or parse in scripts.

printf -- '-type f ! \( %s \)\n' "${files[*]:1}"

Output (which find receives and process).

-type f ! \( -name 1.mp3 -o -name 2.mp3 -o -name 3.mp3 -o -name 4.mp3 -o -name 5.mp3 \)

See the following links:

  •  Tags:  
  • bash
  • Related