How to make a shell script to move files to individual folders based on the filename?

I want to automate moving TV show episode files into individual directories for each episode.

Essentially, in the folder I want to run the script in, I have a list of files. For example: tvshow.2009.S01E01.episodename1.mkv,tvshow.2009.S01E02.episodename2.mkv, tvshow.2009.S01E03.episodename3.mkv, ect. and empty directories in the format E01, E02, E03, ect.

I have been moving the files manually at this point with the help of Tab autocompletion at the command line. Essentially I am typing:

mv t

and then hitting the Tab key to autocomplete out to

mv tv.show.2009.S01E0

then type 1 and Tab again to get to

mv tv.show.2009.S01E01.episodename1.mkv 

then I type the correct folder to get to

mv tv.show.2009.S01E01.episodename1.mkv E01/

and then hit Enter to complete the command. I have to repeat the steps for as many episodes as I have in the original directory, and given the fact I am essentially repeating the same steps and just iterating the episode number, it seems like a perfect use of a for loop in a script.

The issue is that the files are not always in the same format, and will obviously have different names depending on the show. What is constant though is that S##E## will always appear in the filename and I only need to use the E## to identify the correct file and move it into the folder that already exists and exactly matches the E## from the file.

I am an experienced programmer, but have not worked with shell scripts yet. My thought is to maybe iterate through all the existing E## folders, look for a file that contains that E## sequence, and then move that file into the folder. Unfortunately I am not sure the correct approach in doing this with a script that I can run on the command line.

Would anyone have a suggested plan of approach or be able to point me towards a resource I could refer to for help?

2

4 Answers

Here is the solution I came up with thanks to the other answers given

#!/bin/bash
for f in *.mkv *.mp4 *.wmv; do if [ -f "$f" ] # does file exist? then dir=$(echo "$f" | grep -o "E[0-9][0-9]" | head -1) # extract first E## from filename if [ "$dir" ] # check if string found then mkdir -p "$dir" # create dir mv "$f" "$dir" # move file into new dir else echo "INCORRECT FILE FORMAT: \""$f"\"" # print error if file format is unexpected fi fi
done

I had to add some error checking to catch any files that are incorrectly formatted and for double episode files where the filename has two matching E## strings (for example tvshow.S01E20E21.mkv). For double episode files it will create a directory for the first episode number only and move the file to there. Note that this script will make the E## directories as well since it makes it easier to have one script do it all.

It looks to me like you are trying to just copy all E01's into an E01 folder, E02's into an E02 folder etc etc if this is the case a bash script like this should work

#!/bin/bash
cd $(dirname $0)
for orig_filename in ./mkvfiles/*.mkv; do dirname="$(dirname $orig_filename)" filename=${orig_filename##*/} series=${filename%.*} episode=${orig_filename} [[ $episode =~ [eE][0-9][0-9] ]] epi="${BASH_REMATCH[0]}" if [ -n "$epi" ]; then echo ${epi^^} [ ! -d ${epi^^} ] && mkdir -p ${epi^^} || : /bin/cp $orig_filename ${epi^^}/$filename fi
done

The script just iterates all the files, filtering for the Exx (where xx is number) and creates/copies the file to the relevant folder.

You will probably also have to change the ./mkvfiles/*.mkv

You can use this from the directory where you have your files and empty directories, supposing that your files have an .mkv extension:

for file in *.mkv;do mv "$file" "$(echo $file | sed -E 's/.*S[0-9]+(E[0-9]+).*/\1/')";done

In the above for loop, mv is used to move each file ("$file") to the respective episode directory ("$(echo $file | sed -E 's/.*S[0-9]+(E[0-9]+).*/\1/')"). Here, sed is used to extract the episode from the filename. Essentially, it matches the S##E## part of the filename using a regular expression (.*S[0-9]+(E[0-9]+).*), from which only the part inside the parentheses is kept (\1 at the end of sed command).

Initial file structure:

.
├── E01
├── E02
├── E03
├── E04
├── E05
├── tv.show.2009.S01E01.episodename1.mkv
├── tv.show.2009.S01E02.episodename1.mkv
├── tv.show.2009.S01E03.episodename1.mkv
├── tv.show.2009.S01E04.episodename1.mkv
└── tv.show.2009.S01E05.episodename1.mkv

Result:

.
├── E01
│   └── tv.show.2009.S01E01.episodename1.mkv
├── E02
│   └── tv.show.2009.S01E02.episodename1.mkv
├── E03
│   └── tv.show.2009.S01E03.episodename1.mkv
├── E04
│   └── tv.show.2009.S01E04.episodename1.mkv
└── E05 └── tv.show.2009.S01E05.episodename1.mkv

While you mention that you have already created the episode directories, you can create them and also move the files in them with a modification to the command above. The modification just adds mkdir "$(echo $file | sed -E 's/.*S[0-9]+(E[0-9]+).*/\1/')" to the command (the echo part is the same as in the mv before). Thus, the command to create the directories and move the files in them is:

for file in *.mkv;do mkdir "$(echo $file | sed -E 's/.*S[0-9]+(E[0-9]+).*/\1/')" && mv "$file" "$(echo $file | sed -E 's/.*S[0-9]+(E[0-9]+).*/\1/')";done

Initial file structure:

.
├── tv.show.2009.S01E01.episodename1.mkv
├── tv.show.2009.S01E02.episodename1.mkv
├── tv.show.2009.S01E03.episodename1.mkv
├── tv.show.2009.S01E04.episodename1.mkv
└── tv.show.2009.S01E05.episodename1.mkv

Result:

.
├── E01
│   └── tv.show.2009.S01E01.episodename1.mkv
├── E02
│   └── tv.show.2009.S01E02.episodename1.mkv
├── E03
│   └── tv.show.2009.S01E03.episodename1.mkv
├── E04
│   └── tv.show.2009.S01E04.episodename1.mkv
└── E05 └── tv.show.2009.S01E05.episodename1.mkv
0

I think, more or less this can work for you:

#!/bin/bash
for f in *.mkv *.mp4 *.wmv; do # add the extensions you want dir=$(echo "$f" | grep -o "S[0-9][0-9]E[0-9][0-9]") mkdir -p $dir mv $f $dir
done

If you want to run the script just once, you can copy the folder's paths into a file, let's call it list:

/path/to/seriesX
/path/to/seriesY
/path/to/seriesZ

And then in the "root" folder the script:

#!/bin/bash
mapfile -t array < list
for d in "${array[@]}"; do for f in "$d"/*.mkv; do dir="$(echo "$f" | grep -o "S[0-9][0-9]E[0-9][0-9]")" mkdir -p "$d"/"$dir" mv "$f" "$d"/"$dir"/ done
done
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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

You Might Also Like