5

I have several files named like this: This is a test - AB12-1998.avi

(the last code is always 2 letters, 2 numbers, dash, 4 digits)

What I'd like to do is rename them like this: AB12-1998 - This is a test.avi

I'd appreciate any solution you can give me using bash, rename, or any other way as long as it gets the job done.

jesse_b
  • 37,005
Mark Roi
  • 164

2 Answers2

8

With the Perl rename (*):

rename 's/^(.*) - (.*)(\..*)$/$2 - $1$3/' *.avi

or, if you want to be stricter about the code:

rename 's/^(.*) - ([a-zA-Z]{2}\d{2}-\d{4})(\..*)$/$2 - $1$3/' *.avi

That should work even with names like foo - bar - AB12-1234.avi, since the first .* greedily matches up to the final <space><dash><space>.

(* see: What's with all the renames: prename, rename, file-rename?)

Or similarly in Bash:

for f in *.avi ; do
    if [[ "$f" =~  ^(.*)\ -\ (.*)(\..*)$ ]]; then
        mv "$f" "${BASH_REMATCH[2]} - ${BASH_REMATCH[1]}${BASH_REMATCH[3]}"
    fi
done

Briefly, the regexes break down to

^     start of string
( )   capture group
.*    any amount of anything
\.    a literal dot
$     end of string

Most regular characters match themselves, though you need to escape spaces with backslashes in Bash (as above). The contents of the capture groups appear in order in $1, $2 etc in Perl, and in ${BASH_REMATCH[1]}, ${BASH_REMATCH[2]} etc in Bash.

ilkkachu
  • 138,973
  • Rename is the easiest, but rheck that you have the correct rename (The one written by Larry Wall). – ctrl-alt-delor May 19 '18 at 18:53
  • @ctrl-alt-delor thanks for clarifying, I had to use the bash solution as in archlinux the default rename is a different tool; the one by Larry Wall is called perl-rename. – Mark Roi May 20 '18 at 07:00
  • @ikkachu I have now my files in this format: "AB12-1998 - This is a test.avi" and have to change them so that the year comes before, e.g. "1998-AB12 - This is a test.avi" - How do I change your bash solution to do that? – Mark Roi May 23 '18 at 08:43
  • @MarkRoi, Well, it's shouldn't be too hard to modify the above to that. I'd suggest you try it. I added a really short explanation of the special characters I used, and there's a number of resources online about regexes, e.g. regular-expressions.info, regex101.com, even wikipedia – ilkkachu May 23 '18 at 09:21
  • @ikkachu Thanks for the extra info. I already tried changing it myself and failed, regex's are really out of my comprehension. I finally managed to do it using awk's substr function. – Mark Roi May 23 '18 at 10:49
  • @MarkRoi, (.*)-(.*) - (.*) should work to split on the dashes, if you can count on the filenames only having those two. Or (....)-(....) - (.*) to just nip exactly four characters for the first two blocks. Then just reorder the first two captured groups as above. I didn't test these all through, though. – ilkkachu May 23 '18 at 10:57
3

Using /bin/sh:

for name in *.avi; do
    n=${name%.avi}    # name without filename extension
    first=${n%% - *}  # first part of name
    last=${n#* - }    # last part of name
    new="$last - $first.avi"  # recombined new name

    printf 'Would move "%s" to "%s"\n' "$name" "$new"
    # mv -- "$name" "$new"
done

The ${parameter%%word} removes the longest suffix string matching word from $parameter, while ${parameter#word} removes the shortest prefix string matching word from $parameter. The code will therefore swap the two sub-strings on the first occurrence of - (space-dash-space).

Remove the commended out mv to actually rename files.


In comments to ilkkachu's answer, I see that you want to swap the first part of the name as well, so that AB12-1998 becomes 1998-AB12.

Since we're now doing two swap operations on the name, we can put that into a function:

swap () {
    # swaps the two part of a string around
    # the swap point is defined by the first argument

    swstr=$1
    string=$2

    first=${string%%$swstr*}
    last=${string#*$swstr}

    printf '%s%s%s\n' "$last" "$swstr" "$first"
}

for name in *.avi; do
    n=${name%.avi}
    n=$( swap ' - ' "$n" )
    first=${n%% - *}
    first=$( swap '-' "$first" )
    new="$first - ${n#* - }.avi"

    printf 'Would move "%s" to "%s"\n' "$name" "$new"
    # mv -- "$name" "$new"
done

Example output:

Would move "This is a test - AB9-1995.avi" to "1995-AB9 - This is a test.avi"
Would move "This is a test - AB9-1996.avi" to "1996-AB9 - This is a test.avi"
Would move "This is a test - AB9-1997.avi" to "1997-AB9 - This is a test.avi"
Would move "This is a test - AB9-1998.avi" to "1998-AB9 - This is a test.avi"
Would move "This is a test - AB9-1999.avi" to "1999-AB9 - This is a test.avi"
Kusalananda
  • 333,661