4
\$\begingroup\$

I like to collect images for my desktop background, the problem is sometimes the image names don't represent what the image is. I decided to write a script that reads a text file that contains the source of the images to be renamed, and the base name. The script renames the images using the base name and increment counter and moves it to a portable drive.

Picture Database:

Pictures\Landscape,landscape 
Pictures\Batman,batman

Script:

$picture_database_location = "$PSScriptRoot\pictures_database.txt"
$filter_ext = "jpg"
$rootDestination = "R:"


function determineIfFileorDirectoryExists
{    Param([string]$fileordirectory)

    <#
        Determine if a File or Directory Exists. If the file or directory doesn't exists,
        it will throw an exception. 

        Args:
            string: $fileordirectory - the variable that contains the file or directory 
                                       we're going to Test. 

        Returns:
            None
    #>

    $fileordirectoryExists = Test-Path $fileordirectory

    if($fileordirectoryExists -eq $false){
        throw [System.IO.DirectoryNotFoundException] "Not Found: $fileordirectory"
    }
}

function determineargumentCount
{      Param([string[]]$arraytoverify)

    <#
        Determines if the array length is correct. If not, it will throw and display the elements in the array. 

        Args:
            string[]: $arraytoverify - the array to check for length.

        Return:
            None
    #>

    if ($arraytoverify.Length -ne 2){
        throw "Argument count incorrect. # of Parameters: $arraytoverify.Length. `nProvided Parameters: $arraytoverify"
    }
}


try{

    # Before renaming any images we need to verify that the source location and destination exist. 
    determineIfFileorDirectoryExists -fileordirectory "$picture_database_location"
    determineIfFileorDirectoryExists -fileordirectory "$rootDestination" 
    $picture_database = Get-Content $picture_database_location

    ForEach ($image in $picture_database){
        # Construct an array based on the split string
        # $imagaeArray is constructed as follows:
        # $imageArray[0]: The directory that contains the images to rename
        # $imageArray[1]: The base name to rename the images
        $imageArray = $image.Split(",")
        determineargumentCount -arraytoverify $imageArray
        # Construct the Source path with USERPROFILE environment variable and the first element in the array (the directory that contains our pictures)
        $imagePath = Join-Path $env:USERPROFILE -ChildPath $imageArray[0]
        determineIfFileorDirectoryExists -fileordirectory "$imagePath"
        Set-Location -Path "$imagePath"
        # Filter images by type and bulid a list with all the image names
        $fileList = (Get-ChildItem $imagePath -Filter "*.jpg").Name

        if($fileList.Length -gt 0){

            # Construct a string representing the desination path
            $DestinationPath = Join-Path $rootDestination -ChildPath $imageArray[0]
            $doesDirectoryExist = Test-Path $DestinationPath

            if($doesDirectoryExist -eq $false){
                # - ErrorAction Stop - stops the script if the action take fails. 
                md "$DestinationPath" -ErrorAction Stop
                determineIfFileorDirectoryExists -fileordirectory "$DestinationPath"
            }   

            # Get the number of pictures in the destionation and set the counter. 
            [int]$fileCounter = (Get-ChildItem $DestinationPath -Filter "*.jpg").Length
            ForEach ($imagetoRename in $fileList){
                $fileCounter++
                $renamed_file = "{0}_{1}.{2}" -f $imageArray[1],$fileCounter,$filter_ext
                $imageDestination = Join-Path $DestinationPath -ChildPath $renamed_file
                Write-Output "Image : {0} will be renamed to {1}" -f $imagetoRename, $imageDestination 
                Move-Item -Path "$imagetoRename" -Destination "$imageDestination"
            }

        }
   }
   Set-Location $PSScriptRoot     
}

catch [System.IO.DirectoryNotFoundException]
{
    Write-Output $PSItem.Exception.Message
}

catch [System.IO.IOException]
{
    Write-Output $PSItem.Exception.Message
}

catch{
    Write-Output $PSItem.Exception.Message
}

finally{

    exit 
}

Suppose there were two images in each of the source folders, it will rename them like this:

landscape_1.jpg
landscape_2.jpg

batman_1.jpg
batman_2.jpg

Areas of Concern:

  • I use the variable $imageArray but I'm thinking simple classes might make it more readable instead of a bulky comment.
  • I'm aware a nested for loop isn't good for performance, but I'm unsure how to rewrite the code.
\$\endgroup\$
  • \$\begingroup\$ Nice first question. I hope you get good answers. \$\endgroup\$ – brug Jul 21 '18 at 15:33
  • \$\begingroup\$ One style thing that I like to do is that you can turn test-paths into one-liners. if(test-path $path) and negated being if(!(test-path $path)) because it returns a bool \$\endgroup\$ – Veskah Jul 25 '18 at 0:22
2
\$\begingroup\$

The name Picture Database is not quite correct, as it is a Picture Location Database.

I would use a csv to store the elements and use Import-Csv to read data in.

I have a small problem with the assumption that the Picture folder is always a sub-folder of $Env:USERPROFILE as this special folder could be relocated.

The proper way to evaluate the pictures folder location of the currently logged on user is:

[Environment]::GetFolderPath('MyPictures')

If you structure your comment block in the functions with some keywords this can be used by Get-Help

<#
    .SYNOPSIS
    .DESCRIPTION
    .EXAMPLE
    .PARAMETER
    .INPUTS
    .OUTPUTS
    .NOTES
    .LINK
#>

See the link for an advanced function template

I see no problem with stacked for, I'd use a

ForEach ($file in $fileList){...}

to enumerate the $fileList collection.

Some design decisions are always a matter of personal preference.

In general a nice peace of work (+1).

\$\endgroup\$

Your Answer

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