Score:6

Renaming files with a year prefix from the beginning of the filename to the end

ad flag

Extreme novice here attempting to figure out how to rename a bunch of files in a directory. I have hundreds of media files that all follow the same syntax for the file name:

year - title of movie.avi

I would like to write a script that removes the year and the " - " from the beginning of the file name and appends it onto the end. End result:

title of movie - year.avi 

(or something similar. I don't really need the dash)

The problem I am running into with a for loop like the one below is that, since the file names have spaces in them, my script keeps parsing each word instead of each full filename.

for file in $(ls)

I haven't gotten very far as a result.

FedKad avatar
cn flag
Something like `rename -n 's/(\d\d\d\d) - (.*)/$2 - $1/' *`?
Raffa avatar
jp flag
@FedKad I guess you meant `rename -n 's/(\d\d\d\d) - (.*)(\..*)/$2 - $1$3/' *` :-)
Raffa avatar
jp flag
Parsing the output of `ls` is a not so good idea ... and you don't need it ... `for file in *; do ...` will do.
FedKad avatar
cn flag
Thanks @Raffa for the correction. I was just writing from memory on my tablet!
cn flag
One of the rules of thumb with Linux is to never try to parse ls to do something with files. There's always a better tool.
in flag
Unless you specifically want to learn shell scripting then I'd use [krename](https://apps.kde.org/en-gb/krename/) or similar. `shellcheck` tool (try it on shellcheck.net) will show you where you're going wrong, for shell scripts in general, with not using "" to delimit strings.
Score:10
cn flag

You can use rename command with regexp:

rename 's/(\d+) - (.*)\.avi/$2 - $1.avi/' *.avi

explained:

  • rename - command (you may have to install it)
  • s/<match>/<replacement>/ (substitute command)
  • .*avi run above on all .avi files

Match regex:

  • (\d+) matches and captures group ($1) of one or more digits
  • - matches literal -
  • (.*) match anything and capture in group ($2)
  • \.avi match .avi (dot is escaped)

Replacement:

  • $2 - $1.avi - paste second group (title), then dash, then first group (year), then file extension

learn regex, <3 Regex. Steer away from using bash for looping over files, as splitting strings to tokens is hard to understand and often confusing, and you may just overlook some things if you don't understand them.

Raffa avatar
jp flag
+1 Although IMHO limiting digits to 4 (year) would be more precise and capturing the extension in a group would be more portable e.g. `rename -n 's/(\d\d\d\d) - (.*)(\..*)/$2 - $1$3/' *.avi`
Zachary Sycks avatar
ad flag
worked flawlessly! many thanks
Eric Duminil avatar
us flag
Does the regex need to match the whole filename, or can it match a part of it? Would your regex match `some random movie 1234 - whatever.avi`?
Tooster avatar
cn flag
@EricDuminil this regex matches a part of the name, but OP said that this is how his files look like. To match a whole filename from start to finish use start and end of line anchors in a match: `s/^<match>$/<replace>`. A note in reply to the first comment generalization works in this case but it would fail for multipart extensions, like `.tar.gz` for example, so if it's not needed I would leave it in this simple form. Another tip is that you can use `\d{4}` as 4 occurrences of digit instead of `\d\d\d\d`.
in flag
Tooster seems right to use ^ as a file might be, eg "1974 - 2001 - A space Odyssey" or "2009 - Earth 2100 - TV" or "2009 - 1066 - series" of your data isn't known to be well formed. Grep would normally only get the first match per line, but idempotency might be an issue too.
Score:2
ca flag

You could also use mmv for this. First install it using:

sudo apt install mmv

Then from the directory with your files run:

mmv -n '???? - *.avi' '#5 - #1#2#3#4.avi'

The first expression ('???? - *.avi') matches any file that starts with 4 characters (a ? for each one of the 4 digits of the year), is then followed by a hyphen surrounded by spaces (-) and any number of any character (* for the title) and ends with .avi.

The second expression ('#5 - #1#2#3#4.avi') takes each match (which is of the form #n) and arranges it in the way shown to achieve your desired result, i.e the tile first and the year after, separated by -.

The above command will show previews of the changes that will be made. If you are satisfied with them, run the command without the -n flag.

Score:1
jp flag

In bash only:

for f in *.avi; do 
    t="${f#*- }"; t="${t%.*}"; echo mv -n -- "$f" "${t} - ${f%% *}.${f##*.}";
  done
Score:0
ru flag

One thing you can do in Bash or Korn Shell is change the Internal Field Separator. By default, the $IFS includes spaces, tabs, and carriage returns or newlines. You can change the IFS to ONLY use the newline at the end of the line.

Example:

ORIGINAL_IFS="${IFS}"
IFS=$(echo "\n")
for LOOP in $(ls)
do
  print ${LOOP}
done
IFS="${ORIGINAL_IFS}"

Instead of just using print at that stage in the loop, you can use awk or cut and grab the year off the beginning of the line.

You may want to reset the IFS back to its original value before moving on to the next part of the script, in case it affects other logic.

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.