How can I rename directories from “[YYYY] Title” or “(YYYY) Title” to “YYYY - Title”?
Asked by
MellowCedar42
I have a directory containing several subdirectories whose names begin with a four-digit year. Each name follows either "[YYYY] Title" or "(YYYY) Title", and I want to rename them to "YYYY - Title". I'm still learning Bash and was considering looping over the directories, extracting the year with a regular expression, and then using mv to apply the new name. What's a safe and reasonably simple way to do this?
Answers
If you prefer regular expressions, Bash stores captured groups in `BASH_REMATCH`. This matches either kind of opening and closing bracket and captures the year and title:
```bash
for d in */; do
d=${d%/}
if [[ $d =~ ^[([0-9]{4})] (.*)$ || $d =~ ^(([0-9]{4})) (.*)$ ]]; then
year=${BASH_REMATCH[1]}
title=${BASH_REMATCH[2]}
new="$year - $title"
printf '%q -> %qn' "$d" "$new"
# mv -- "$d" "$new"
fi
done
```
The `*/` glob limits the loop to entries that look like directories, and removing the trailing slash makes the string easier to work with. Test with `printf` before enabling `mv`, especially if two source directories could produce the same destination name.
Reply to this answer
For a small number of directories, an interactive tool such as `vidir` can be less error-prone. It opens a listing in your editor; you change the names in the listing and save it, and the tool performs the corresponding renames. That gives you a chance to review every change without writing a script.
Reply to this answer
You can do this entirely in Bash with a pattern loop and parameter expansion. This version only considers directories, handles both bracket styles, prints each change first, and avoids overwriting an existing destination:
```bash
for d in ([0-9][0-9][0-9][0-9]) * [[0-9][0-9][0-9][0-9]] *; do
[ -d "$d" ] || continue
year="${d:1:4}"
title="${d:7}"
new="${year} - ${title}"
if [ -e "$new" ]; then
printf 'Skipping: destination already exists: %sn' "$new" >&2
continue
fi
printf 'Would rename: %q -> %qn' "$d" "$new"
# mv -- "$d" "$new"
done
```
Once the output looks right, uncomment the `mv` line. Keeping the old name and new name quoted is important because titles may contain spaces or shell characters.
Reply to this answer
Add your answer
Answers are reviewed before publication.
BrightPanda18: The `[ -d "$d" ]` check is useful here because the glob can also remain literal when nothing matches, and it prevents accidentally processing regular files.