[Bash] Move files/movies from a single directory to their own sub-directory!

After spending a couple hours moving all my movies from a single massive folder to their own sub-directories, I decided “SCREW THIS” and decided to create a bash script.

I got my solution from trawling through various Stack Overflow posts on Bash posts. Apologies, I should have noted the references but didn’t think this would be so easy! I also don’t have enough rep to vote up either (rather annoying!).

As you might have noticed from my other scripts, I like to log things, so everything gets logged :D

Move all movies and their associated files into their own sub-directory, taking into account multi-part files (e.g. “Movie CD01/CD02”).

Sounds a bit naff but this script relies on one thing: The movie name is consistent across each movie, i.e. the movies are already neatly named. If you use SABNzbd/Couchpotato, this shouldn’t be a problem. To give you an example, all my movies are named as follows: “Winnie The Pooh (2011) (720p).mkv” or in the case of multi-parts “Annie (1982) CD1.avi” and “Annie (1982) CD2.avi”.

How To Use
Pretty simple, start up a shell (be it via PuTTY or KiTTYTerm as I now use), “cd” to the Movie folder and paste this sucker in.

echo "Folder creation stated" > folders_created.log
find ./ -maxdepth 1 \( -name "*.avi" -o -name "*.mkv" -o -name "*.wmv" -o -name "*.mp4" -o -name "*.ts" -o -name "*.mpg" \)  -type f -print0 | while IFS= read -d '' file
    # extract the name of the directory to create
    dirName=`echo ${file%.*} | sed -r 's/\sCD[0-9]*//g'`

    # create the directory if it doesn't exist
    [[ ! -d "$dirName" ]] && mkdir "$dirName" && echo "Making directory $dirName" >> folders_created.log

    mv -v "$dirName"* "$dirName" 2>&1 | tee -a folders_created.log

Script Rundown

  • Basically it goes through the current directory, finds every AVI, MKV, WMV, MP4, TS and MPG file.
  • For each file it finds, it …
    • Strips the multi-part identifier. This can be either CD1 or CD02 or CD99, and combination of single or double digit from 0 to 99;
    • Checks to see if the folder exists, if it doesn’t create it using the movie name excluding the multi-part number (and logs it);
    • Moves (verbosely) all files that start with the movie title into the new directory (and logs it!).

and that’s it!

Next I have to re-scan using XBMC then clean my library to remove the old references and I’m all done! YIPPIE KAI YAY!

If you like this, then go donate to some kitten patting charity.

[Powershell] Copy and rename files as defined in a CSV mapping file

Ha! What a title. I have no idea how else to describe it.

This is really a Part 2 to my earlier blog post, Powershell: Create files in a directory from a CSV list

In that post, I created a massive list of documents. Now the script I really needed was to rename them based on a mapping in a CSV document.


  • Copy a file from one directory to another, renaming them based on a mapping CSV document;
  • Actions get logged;
  • Check file exists before copying, log then skip
  • Log if the file is being overwritten or not

CSV File Headers


The Powershell Code:

$mappinglist = ".\mapping.csv"
$sourcedocs = "\path\to\source\folder"
$destdocs = "\path\to\destination\folder"
$date = get-date -format yyyyMMddHHmm
$logfile = ".\$date-documentcopy.log"

Add-Content $logfile "### $date Starting Document Copying ###"
Import-Csv $mappinglist | Foreach-Object { 
  $srcfile = $_.SourceFile ;
  $destfile= $_.DestinationFile;

  If (!(Test-Path $destdocs\$destfile)) {
    Add-Content $logfile "$srcfile ($destfile) not found in destination directory"
    Write-Host "$srcfile ($destfile) not found in destination directory"
  if (Test-Path $sourcedocs\$srcfile) {
    Write-Host "$srcfile copying to $destdocs\$destfile"
    Copy-Item $sourcedocs\$srcfile $destdocs\$destfile"
    Add-Content $logfile "$srcfile copied to $destdocs\$destfile"
  } else {
    Write-Host "$srcfile source document not found ... skipping"
    Add-Content $logfile "$srcfile source document not found ... skipping"
Add-Content $logfile "### $date Finished Document Copying  ###"

Most of the code is done in a few lines, however for my purpose, I really needed to have everything logged as we generate formal reports on basically everything we do!
I’m a big fan of timestamping my log files, hence the $date-documentcopy.log line.
Also, I like being able to see what’s going AND having it log to file, so I duplicate the Add-Content and the Write-Host lines.
I rather like the Write-Host “copying” and Add-Content “copied” line. The reason for this is I have some large files (>2GB) so *if* it fails I can see on-screen which record it was but the log has it once it’s finished.

[Powershell] Create files in a directory from a CSV list

I’ve recently been using Powershell more and more to replace either broken and/or ageing ‘batch’ files at work plus also come personal projects I’ve been working on.

Some of them took a bit of digging around between various sites/blogs to put together, so I’ve decided to re-post them with a final solution.

Warning: I’m a pretty crap blogger, so don’t expect regular updates!

Anyway, this one is I wanted to test a copy and rename script but needed some testing data first. So this creates a file in a folder with the name taken from a list in a CSV. The ID didn’t have a file extension so I’m randomly creating one from a small list.

Brief: Take a list of ID’s from a CSV and create a file in a folder using the ID as the filename with a random file extension.

$docext = @("msg","docx","doc","xlsx","xls","pdf")

$path = ".\filename_list.csv"

Import-Csv $path | Foreach-Object { 
  $filename = $_.filename;
  $ext = $docext | Get-Random -Count 1;
  if (!(Test-Path "C:\path\to\files\$filename.*")) {
    $filename | Out-File "C:\path\to\files\$filename.$ext";
    Write-host "creating file $filename";
  } else {
    Write-Host "file already exists";

A brief run down.
The @docext is an array of possible extensions, as mine list only has an ID, not the extension. I wanted to make one up for my test.
The $ext = $docext | Get-Random -Count 1; grabs one of these random extensions.
I run a Test-Path to see if the filename exists so it doesn’t create two files with different extensions. I use a wildcard to check.
Lastly I Write-Host so I can see what’s going on.