3
\$\begingroup\$

Here's the current structure of my directory.

.
├── Show 1
│  ├── Season 1
│  └── Season 2
├── Show 2
  ├── Season 1
  └── Season 2

I want to rename the Season folders to include the name of the Show. The desired structure looks like this :

.
├── Show 1
│  ├── Show 1 - Season 1
│  └── Show 1 - Season 2
├── Show 2
  ├── Show 2 - Season 1
  └── Show 2 - Season 2 

Here's the script I wrote :

# Parse through all show folders.
for show in /Users/sanjeetsuhag/Desktop/* ; do
 # Check if it is a folder.
 if [ -d "$show" ]; then
 # Parse through all season folders.
 for season in $show/* ; do
 # Check if it is a folder.
 if [ -d "$season" ]; then 
 mv $season "$show/$(basename "$show") - $(basename "$season")" 
 fi 
 done
 fi
done

This is my first time scripting in Bash. Is there any thing I could improve ?

200_success
146k22 gold badges191 silver badges481 bronze badges
asked Jan 1, 2017 at 13:28
\$\endgroup\$
1
  • \$\begingroup\$ The * expansion on your for lines will cause problems if you have a space in a file name or directory since the shell splits things on whitespace. \$\endgroup\$ Commented Jan 1, 2017 at 15:34

2 Answers 2

3
\$\begingroup\$

You should always double-quote variables that are used as filesystem paths. This is the same script but with variables correctly double-quoted:

# Parse through all show folders.
for show in /Users/sanjeetsuhag/Desktop/*; do
 # Check if it is a folder.
 if [ -d "$show" ]; then
 # Parse through all season folders.
 for season in "$show"/*; do
 # Check if it is a folder.
 if [ -d "$season" ]; then 
 mv "$season" "$show/$(basename "$show") - $(basename "$season")" 
 fi 
 done
 fi
done

If you change the globs to end with /, they will match only directories, and so you can skip the directory checks:

for show in /Users/sanjeetsuhag/Desktop/*/; do
 for season in "$show"/*/; do
 mv "$season" "$show/$(basename "$show") - $(basename "$season")" 
 done
done

You could further optimize this by using pattern substitution instead of basename:

for show in /Users/sanjeetsuhag/Desktop/*/; do
 for season in "$show"/*/; do
 show=${show%/}
 season=${season%/}
 mv "$season" "$show/${show##*/} - ${season##*/}"
 done
done

The ${show%/} is to shave off the trailing /, and the ${show##*/} is to delete everything until the last /.

Finally, instead of hardcoding the base path /Users/sanjeetsuhag/Desktop, the script would be more reusable if you make that a command line parameter of the script.

answered Jan 17, 2017 at 22:15
\$\endgroup\$
1
\$\begingroup\$

To avoid problems with spaces, as warned by @chicks, and most other hazardous characters as well, use double quotes often. The find utility also has a null-terminated line option -print0 that helps with space and newline problems.

Here's a revamped script to try. Liberally commented as this is your first time into Bash scripting. Hope it helps you learn, and works as intended for your project.

#!/bin/bash
# Convert this directory structure:
# .
# ├── Show 1
# │ ├── Season 1
# │ └── Season 2
# └── Show 2
# ├── Season 1
# └── Season 2
# to this structure:
# .
# ├── Show 1
# │ ├── Show 1 - Season 1
# │ └── Show 1 - Season 2
# └── Show 2
# ├── Show 2 - Season 1
# └── Show 2 - Season 2 
# or, optionally to this structure:
# .
# ├── Show 1
# │ ├── Show 1 - Season 1
# │ │ ├── Show 1 - Season 1 - Episode 1
# │ │ ├── Show 1 - Season 1 - Episode 2
# │ │ └── Show 1 - Season 1 - Episode 3
# │ └── Show 1 - Season 2
# │ ├── Show 1 - Season 1 - Episode 1
# │ ├── Show 1 - Season 2 - Episode 2
# │ └── Show 1 - Season 2 - Episode 3
# └── Show 2
# ├── Show 2 - Season 1
# │ ├── Show 2 - Season 1 - Episode 1
# │ ├── Show 2 - Season 1 - Episode 2
# │ └── Show 2 - Season 1 - Episode 3
# └── Show 2 - Season 2
# ├── Show 2 - Season 2 - Episode 1
# ├── Show 2 - Season 2 - Episode 2
# └── Show 2 - Season 2 - Episode 3
# Is safe for spaces, and most other shell characters. Notable exception
# is the "\" charcter which WILL NOT work in the directoy names anywhere.
# !@#$&*"'{}[](): are all safe, seemingly international chars are as well.
#
# TEST on a backup copy first!! YMMV
#
# Get the show directory names using find
# -maxdepth 1 : limit search to 1 level - the current directory
# return values are ./show
# -type d : only find directories
# -print0 : print the list as null-terminate strings
# pipe stdout to read 
# -d $'0円' : make read use null as the line delimiter
find . -maxdepth 1 -type d -print0 | while read -d $'0円' show;
do
 # Skip the . directory entry for the shows
 if [ ! "." = "$show" ];
 then
 # Get the season directory names using find as above
 # This time use the current show directory as the base directory
 # Return values are ./show/season
 find "$show" -maxdepth 1 -type d -print0 | while read -d $'0円' show_season;
 do
 # Strip of path from the show, in this case just ./
 show=$(basename "$show");
 # Skip the . directory entry for the seasons
 if [ ! "./$show" = "$show_season" ]
 then
 # Strip of the path from the season, in this case ./show/
 season_name=$(basename "$show_season");
 # Section to process the episodes in each season directory under the same name expansion scheme
 # Simply remove the comment marks in the first column, leave those that are deeper in.
 # Get the episode file names using the above logic again
 # This time use the current season directory as the base directory
 # Return values are ./show/season/episode
# find "$show_season" -maxdepth 1 -type f -print0 | while read -d $'0円' show_season_episode;
# do
# if [ ! "$show_season" = "$show_season_episode" ]
# then
# # Strip of the path from the episode, in this case ./show/season/
# episode_name=$(basename "$show_season_episode");
# # Rename the episode, the season directory has NOT been renamed yet!
# mv "$show_season_episode" "$show_season/$show - $season_name - $episode_name";
# fi
# done
 mv "$show_season" "$show/$show - $season_name";
 fi
 done
 fi
done
answered Jan 11, 2017 at 9:50
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.