I'm writing a bash script that does the following:
- In the source directory find all subfolders
- Within those subfolders find the newest mp3 file
- Copy this mp3 file to the destination folder
- 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 GNUfind
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: