Rename file in Mac OS Terminal using Regex or translate from Windows Script

I would like help with a shell script for macOS to rename files with a certain pattern. The script would handle only one file, not multiple.

Here are three examples:

a. Public Talks_ (189) Walking With God Brings Blessings Now and Forever — Chris Ruscher 10_28_2023.mp3

would become

189 – Walking With God Brings Blessings Now and Forever – Chris Ruscher – 2023-10-28-0900.mp3

b. Public Talks_ (55) How Can You Make a Good Name With God? — Gregory Duhon 11_4_2023.mp3

would become

055 – How Can You Make a Good Name With God? – Gregory Duhon – 2023-11-04-0900.mp3

c. Public Talks_ (9) Walking With God Brings Blessings Now and Forever — Chris Ruscher 10_28_2023.mp3

would become

009 – Walking With God Brings Blessings Now and Forever – Chris Ruscher – 2023-10-28-0900.mp3

So in essence, the final format and order should be: track number, title, speaker name, and timestamp.

  1. drop the prefix “Public Talks_ “
  2. Isolate the track number as a 3 digit number with leading zeros as needed
  3. Dashes in between all 4 elements
  4. Reformat date to a timestamp like yyyy-mm-dd-0900

I would then take the shell script and use it per file in this dialog box (no need to iterate):

Please note the variable for the file being passed on to the shell script needs to be written as "$1" as seen in the dialog box below.

insert shell script window

Someone helped me create one but it only works for Windows and doesn’t take into account "$1" as the file and is set to do multiple files. I need it to work on macOS for one file at a time.

#!/bin/bash
for file in *.mp3; do
    # Remove "Public Talks_"
    newname=${file#"Public Talks_ "}
    # Extract the track number and pad with leading zeros
    track=$(echo $newname | grep -o -E '([0-9]+)' | tr -d '()' | awk '{printf "%03dn", $0}')
    # Remove track number and trailing spaces
    newname=$(echo $newname | sed -E 's/([0-9]+)//' | sed 's/^ *//')
    # Extract the title and speaker
    title=$(echo $newname | awk -F '—' '{print $1}' | sed 's/ *$//')
    speaker=$(echo $newname | awk -F '—' '{print $2}' | awk '{print $1, $2}')
    # Extract the date and reformat
    date=$(echo $newname | grep -o -E '[0-9]+_[0-9]+_[0-9]+' | tr '_' '-' | awk -F- '{print $3"-"$1"-"$2"-0900"}')
    # Concatenate all elements with dashes
    newname="$track - $title - $speaker - $date.mp3"
    # Rename the file
    mv "$file" "$newname"
done

Can anyone translate it into the proper macOS syntax or write a shell script that is even more concise?

Asked By: Allex Valentin

||

In zsh, from within the directory that contains those files, you’d run:

autoload -Uz zmv
zmv -n '*_ ((<0-999>))(* )—( * )(<1-12>)_(<1-31>)_(<1900-2100>)(.mp3)' 
       '${(l[3][0])1} -$2-$3- $6-${(l[2][0])5}-${(l[2][0])4}-0900$7'

Example:

$ autoload -Uz zmv
$ zmv -n '*_ ((<0-999>))(* )—( * )(<1-12>)_(<1-31>)_(<1900-2100>)(.mp3)' 
         '${(l[3][0])1} -$2-$3- $6-${(l[2][0])5}-${(l[2][0])4}-0900$7'
mv -- 'Public Talks_ (189) Walking With God Brings Blessings Now and Forever — Chris Ruscher 10_28_2023.mp3' '189 - Walking With God Brings Blessings Now and Forever - Chris Ruscher - 2023-28-10-0900.mp3'
mv -- 'Public Talks_ (55) How Can You Make a Good Name With God? — Gregory Duhon 11_4_2023.mp3' '055 - How Can You Make a Good Name With God? - Gregory Duhon - 2023-4-11-0900.mp3'
mv -- 'Public Talks_ (9) Walking With God Brings Blessings Now and Forever — Chris Ruscher 10_28_2023.mp3' '009 - Walking With God Brings Blessings Now and Forever - Chris Ruscher - 2023-28-10-0900.mp3'

Remove the -n (dry-run) if happy.

If that has to be a shell script that takes the file names as arguments:

#! /bin/zsh -
set -o extendedglob
pattern='(#b)*_ ((<0-999>))(* )—( * )(<1-12>)_(<1-31>)_(<1900-2100>)(.mp3)'
ret=0
for file {
  if [[ $file = $~pattern ]] {
    argv=( "$match[@]" )
    mv -i -- $file "${(l[3][0])1} -$2-$3- $6-${(l[2][0])5}-${(l[2][0])4}-0900$7" || ret=$?
  } else {
    print -ru2 "Skipping $file which doesn't match the pattern"
  }
}
exit $ret

But you’d be missing on the extra safeguards of zmv.

Note that we’re looping over all the arguments of the script instead of just processing the first ($1).

Answered By: Stéphane Chazelas