I've explored a solution to this using TXR Lisp. It's a bit verbose in its present state. It parses the names into structures.
For the requirement that there may be multiple directories which will result in clashing names, we should just deal with all the directories simultaneously. I prepared this example set of paths:
path/a/photo_6923@06-01-2022_14-18-36.jpg path/a/photo_6924@07-01-2022_00-03-23.jpg path/a/photo_6925@07-01-2022_01-36-20.jpg path/a/photo_6926@07-01-2022_10-44-20.jpg path/a/photo_6927@07-01-2022_10-44-20.jpg path/to/b/photo_6923@06-01-2023_14-18-36.jpg path/to/b/photo_6924@07-01-2023_00-03-23.jpg path/to/b/photo_6925@07-01-2023_01-36-20.jpg path/to/b/photo_6926@07-01-2023_10-44-20.jpg path/to/b/photo_6927@07-01-2022_10-44-20.jpg
We have names in a path/a directory and a path/to/b directory. There are clashing entries that have the same time/date 07-01-2022_10-44-20.
In the real program we would use some glob expression to get the names like:
(glob "{path/a,path/to/b}/photo_*.jpg")
rather than reading from a file, and we could replace the dummy-rename function with rename-path.
Run:
$ txr rename.tl path/to/b/photo_6926@07-01-2023_10-44-20.jpg -> path/to/b/07_01_2023_10h44m20s path/a/photo_6926@07-01-2022_10-44-20.jpg -> path/a/07_01_2022_10h44m20s_00000 path/a/photo_6927@07-01-2022_10-44-20.jpg -> path/a/07_01_2022_10h44m20s_00001 path/to/b/photo_6927@07-01-2022_10-44-20.jpg -> path/to/b/07_01_2022_10h44m20s_00002 path/to/b/photo_6924@07-01-2023_00-03-23.jpg -> path/to/b/07_01_2023_00h03m23s path/a/photo_6924@07-01-2022_00-03-23.jpg -> path/a/07_01_2022_00h03m23s path/to/b/photo_6925@07-01-2023_01-36-20.jpg -> path/to/b/07_01_2023_01h36m20s path/a/photo_6925@07-01-2022_01-36-20.jpg -> path/a/07_01_2022_01h36m20s path/to/b/photo_6923@06-01-2023_14-18-36.jpg -> path/to/b/06_01_2023_14h18m36s path/a/photo_6923@06-01-2022_14-18-36.jpg -> path/a/06_01_2022_14h18m36s
Code in rename.tl:
(defstruct name () orig dir number time (:method fmt (me) (let ((tm me.time)) `@{tm.month}_@{tm.day}_@{tm.year}_@{tm.hour}h@{tm.min}m@{tm.sec}s`))) (defun parse (str) (let ((dir (dir-name str)) (base (base-name str))) (match `photo_@num\@@mm-@dd-@{yyyy}_@HH-@[email protected]` base (new name orig str dir dir number num time (new time year yyyy month mm day dd hour HH min MM sec SS))))) (defun dummy-rename (from dir to) (put-line `@from -> @(path-cat dir to)`)) (flow (file-get-lines "data") (mapcar parse) (group-by .time) (dohash (date part @1) (if (eql 1 (len part)) (let ((n (first part))) (dummy-rename n.orig n.dir n.(fmt))) (each ((n part) (i "00000".."99999")) (dummy-rename n.orig n.dir `@{n.(fmt)}_@i`)))))
Only the files that clash to the same date/time get the incrementing stamp. Since this is across multiple directories, we don't need five digits.
The core of the algorithm is to take the parsed objects and group them into groups based on the date. For those groups which only contain one element, we format the name without the additional counters. Within the groups of two or more, we iterate them, while iterating over 00000 to 99999 in parallel, and tack that on as a suffix.
The renames aren't sorted, since group-by produces a hash table. We can see the organization better with a sort:
$ txr rename.tl | sort path/a/photo_6923@06-01-2022_14-18-36.jpg -> path/a/06_01_2022_14h18m36s path/a/photo_6924@07-01-2022_00-03-23.jpg -> path/a/07_01_2022_00h03m23s path/a/photo_6925@07-01-2022_01-36-20.jpg -> path/a/07_01_2022_01h36m20s path/a/photo_6926@07-01-2022_10-44-20.jpg -> path/a/07_01_2022_10h44m20s_00000 path/a/photo_6927@07-01-2022_10-44-20.jpg -> path/a/07_01_2022_10h44m20s_00001 path/to/b/photo_6923@06-01-2023_14-18-36.jpg -> path/to/b/06_01_2023_14h18m36s path/to/b/photo_6924@07-01-2023_00-03-23.jpg -> path/to/b/07_01_2023_00h03m23s path/to/b/photo_6925@07-01-2023_01-36-20.jpg -> path/to/b/07_01_2023_01h36m20s path/to/b/photo_6926@07-01-2023_10-44-20.jpg -> path/to/b/07_01_2023_10h44m20s path/to/b/photo_6927@07-01-2022_10-44-20.jpg -> path/to/b/07_01_2022_10h44m20s_00002
We can clearly see that in the second directory, the clash is avoided by the name 07_01_2022_10h44m20s_00002.
06-01-2022is 6th January or 1st June.07_01_2022rather than2022_07_01?MDYis not anglosaxon, it's more American; Britain (including England) and Saxony use mainlyDMY. See en.wikipedia.org/wiki/List_of_date_formats_by_country