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?
3 Answers
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.
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.
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.

The `[ -d "$d" ]` check is useful here because the glob can also remain literal when nothing matches, and it prevents accidentally processing regular files.