macOS Bash Script – How to Search, Copy, and Rename Files in macOS Using Bash Script

bashfile-transfermacosscript

So I'm trying to recursively search folder, and as you might imagine it will be the "Photos"-folder in MacOS.

And in the process, I would like to change the file name, why you might ask? Because Nextcloud is for some reason changing the image name to year-month-day hour-minute-second $(increment number). So I need to be able to define this starting increment number.

This is what I have so far:

# Lets say that starting increment number should be I=7398
# name="$(stat -f '%Sm' -t '%y-%m-%d %H-%M-%S' "$file").$(ls "$file"|cut -d . -f2)"
# echo $name
find . -type f -iname '*.jpg' -size +500k -exec sh -c '
    for file in "$@"; do
        folder="/path/to/backup/folder/$(stat -f '%Sm' -t '%Y/%m' "$file")"
        echo mkdir -p "$folder"
        echo cp "$file" "$folder"
    done
' sh {} +

This works perfect! But I would like to in the process change the name from the files EXIF data, to be '%y-%m-%d %H-%M-%S {increment number}'.ext, 18-08-15 18-32-29 3453.JPG or whatever it is. Is this possible?

This above script reads a JPG, creates directory of the year the file image was created, created another folder corresponding to the month, copies the file to that year folder.

Expected result: also change name '%y-%m-%d %H-%M-%S {increment number}'.ext

Best Answer

The following is a proof of concept to show how I'd build out the target_directory and target_filename from the source_filename, based on information in your OP and comments:

$ ls -1
DSC_0008.JPG
DSC_0034.JPG
DSC_0077.JPG
DSC_0132.JPG
codetest

$ cat codetest
#!/bin/bash

find . -type f -iname '*.jpg' -size +500k -exec sh -c '
i=7398
for f in "$@"; do
    m="$(exiftool -d "%Y-%m-%d %H-%M-%S" -FileModifyDate -s -S "$f")"
    d="/path/to/backup/directory/${m:0:4}/${m:5:+2}"
    echo mkdir -p "$d"
    echo cp "$f" "$d/$m $i.${f##*.}"
    ((i++))
done
' sh {} +

$ ./codetest
mkdir -p /path/to/backup/directory/2018/11
cp ./DSC_0008.JPG /path/to/backup/directory/2018/11/2018-11-26 15-39-09 7398.JPG
mkdir -p /path/to/backup/directory/2018/12
cp ./DSC_0034.JPG /path/to/backup/directory/2018/12/2018-12-26 11-52-30 7399.JPG
mkdir -p /path/to/backup/directory/2019/03
cp ./DSC_0077.JPG /path/to/backup/directory/2019/03/2019-03-02 08-36-07 7400.JPG
mkdir -p /path/to/backup/directory/2019/04
cp ./DSC_0132.JPG /path/to/backup/directory/2019/04/2019-04-03 23-42-36 7401.JPG
$ 

Obviously with the echo command in place, the output shown is not quoted and would fail if that was the actual command executed while copying; however, with the echo command removed, the actual executed command has quotes around the arguments and would not fail because of the spaces in the target_filenames.


Using stat instead of exiftool:

#!/bin/bash

find . -type f -iname '*.jpg' -size +500k -exec sh -c '
i=7398
for f in "$@"; do
    m="$(stat -f "%Sm" -t "%Y-%m-%d %H-%M-%S" "$f")"
    d="/path/to/backup/directory/${m:0:4}/${m:5:+2}"
    echo mkdir -p "$d"
    echo cp "$f" "$d/$m $i.${f##*.}"
    ((i++))
done
' sh {} +

Note: Both of these worked when run as either a script, or, sans the shebang, copied and pasted into Terminal. That said, I'm not sure I'd run this on 30K files all at once without adding some error handling as appropriate for what this code will do. I'll leave that up to you to decide how, or if, you want to implement any error handling in the example code shown.