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 ?

\$\endgroup\$
  • \$\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\$ – chicks Jan 1 '17 at 15:34
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.

| improve this answer | |
\$\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
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.