bashpattern-matchingbatch-rename

rename multiple folders under different conditions by matching regex patterns in a bash script?


As an example, let's say I have a folder containing these folders:

Universal 2023 02 15 Some Name
Universal 2023 02 15 Some Name and Words After
Sony Some Name 2023 02 15
Sony Some Name 2023 02 15 and Words After

Desired output

Some Name - 2023 02 15 - Universal
Some Name - 2023 02 15 - And Words After - Universal
Some Name - 2023 02 15 - Sony
Some Name - 2023 02 15 - and Words After – Sony

I wrote a command for every name structure.

1. « Universal 2023 02 15 Some Name » will be renamed: « Some Name - 2023 02 15 - Universal » With this command:

rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+))/$6 - $2 - $1/g' *

« Universal 2023 02 15 Some Name and Words After » will be renamed: « Some Name - 2023 02 15 - And Words After - Universal » With this command:

rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+)/$7 - $2 - $10 - $1/g' *

« Sony Some Name 2023 02 15 » will be renamed : « Some Name - 2023 02 15 - Sony » With this command :

rename -v 's/([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2}))/$2 - $5 - $1/g' *
  1. Finally, « Sony Some Name 2023 02 15 and Words After » will be renamed : « Some Name - 2023 02 15 - and Words After - Sony » With this command :
rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g' *

When I want to rename these folders, I have to put them in separate folders and run the corresponding command, then put them all back in the same folder when I'm done. This is very annoying. So I thought of writing a script in bash to avoid having to file them separately and have everything done in the main folder. In the VS code, everything seems to work fine except for the renaming commands. This line is colored orange... Which means that something is missing but I don't know what it is:

's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g'

See this link to view the scipt in VS code colors: https://i.sstatic.net/tosSv.png

My script :

for i in $*/; do
        # for Universal 2023 02 15 Some Name
        if [[ "$i" =~ ([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+)) ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+))/$6 - $2 - $1/g' *
        
        # for Universal 2023 02 15 Some Name and Words After
        elif [[ "$i" =~ ([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+) * ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+)/$7 - $2 - $10 - $1/g' *

        # for Sony Some Name 2023 02 15
        elif [[ "$i" =~ ([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2})) ]];
                then
                        rename -v 's/([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2}))/$2 - $5 - $1/g' *
        
        # for Sony Some Name 2023 02 15 and Words After
        else [[ "$i" =~ ([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2})) ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g' *
        
        fi

done

the script in color for VS code. My commands are all orange...

Anyone can help me please!!!!!!!! Many Thanks! Martin


Solution

  • Try this Shellcheck-clean code:

    #! /bin/bash -p
    
    sep_rx='[[:space:]]+'
    part_rx='[^[:space:]]+'
    company_rx=$part_rx
    name_rx="${part_rx}${sep_rx}${part_rx}"
    date_rx="[[:digit:]]{4}${sep_rx}[[:digit:]]{2}${sep_rx}[[:digit:]]{2}"
    after_rx="${part_rx}(${sep_rx}${part_rx})*"
    
    cdn_rx="^($company_rx)$sep_rx($date_rx)$sep_rx($name_rx)\$"
    cdna_rx="^($company_rx)$sep_rx($date_rx)$sep_rx($name_rx)$sep_rx($after_rx)\$"
    cnd_rx="^($company_rx)$sep_rx($name_rx)$sep_rx($date_rx)\$"
    cnda_rx="^($company_rx)$sep_rx($name_rx)$sep_rx($date_rx)$sep_rx($after_rx)\$"
    
    for d in */; do
        dir=${d%/}
        if [[ $dir =~ $cdn_rx ]]; then
            company=${BASH_REMATCH[1]}
            date=${BASH_REMATCH[2]}
            name=${BASH_REMATCH[3]}
            newdir="$name - $date - $company"
        elif [[ $dir =~ $cdna_rx ]]; then
            company=${BASH_REMATCH[1]}
            date=${BASH_REMATCH[2]}
            name=${BASH_REMATCH[3]}
            words_after=${BASH_REMATCH[4]}
            newdir="$name - $date - $words_after - $company"
        elif [[ $dir =~ $cnd_rx ]]; then
            company=${BASH_REMATCH[1]}
            name=${BASH_REMATCH[2]}
            date=${BASH_REMATCH[3]}
            newdir="$name - $date - $company"
        elif [[ $dir =~ $cnda_rx ]]; then
            company=${BASH_REMATCH[1]}
            name=${BASH_REMATCH[2]}
            date=${BASH_REMATCH[3]}
            words_after=${BASH_REMATCH[4]}
            newdir="$name - $date - $words_after - $company"
        else
            printf 'ERROR: Failed to match: %s\n' "$dir" >&2
            exit 1
        fi
        mv -v -- "$dir" "$newdir"
    done