dark mode

Rename multiple files in multiple folders with PowerShell

Rename multiple files in multiple folders with PowerShell

PowerShell is a powerful tool for every windows power user. I need an easy way to rename all images within a project.

If we run the final code at the end, it will rename all image files in all sub folders. Their names will be the same as its directories with an incremented number as a prefix which reset in every new folder. For example, if the folder named New-Folder contains 3 images the script will rename them as:

01-New-Folder.jpg
02-New-Folder.jpg
03-New-Folder.jpg

First, we need to open Notepad or other editor and save the empty file with PowerShell .ps1 extension for example create a file called run.ps1. If you open PowerShell terminal and run a script, you will get the following message:

PS C:\> .\run.ps1
.\run.ps1 : File C:\run.ps1 cannot be loaded because running scripts is disabled on this system.
 For more information, see about_Execution_Policies at https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ .\run.ps1
+ ~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

It’s a good thing though because PowerShell scripts are a powerful and dangerous thing and it can be malicious.

TIP: Never run a PowerShell script without knowing what is every single thing otherwise you can end up with missing files, a broken system or worst. That is why I will explain you everything line by line.

We will need to declare some initial variables and constants:

$path = "project\\test"
$fileTypes = ".*.jpg|.*.bmp|.*.png|.*.gif|.*.tif"
$files = Get-ChildItem -Recurse | Where-Object FullName -Match ".*$path*"
$counter = 1
$dir = ""

$path will be the relative path from where we will want to manipulate data. In our case we put the file in a project folder and we want to do everything only in the sub folder test and its sub folders.

$fileTypes will be a RegEx (Regular expression) getting all images that have an extension of .jpg, .bmp, .png, .gif or .tif. You can add or change to whatever valid RegEx you want.

$files is a little more complex:

  1. Get-ChildItem will get all files in a folder and the parameter -Recurse will loop through all sub folders.
  2. Pipeline (|) is an operator which will pass one by one all objects from the left side to the command on the right.
  3. Where-Object will be our filter. First, I'm getting FullName because it is a string containing the full path and the filename. Having this data and with the help of the RegEx we are matching $path with FullName.

Now we can say that $files is a collection or an array of all files in the folders we want. To start rename images, we will loop through them with foreach ($file in $files) { }.

Within the loop we need the filename as $name the full path (including the filename and the extension) as $fullname and the file type as $extension.

Now we need a conditional which match the looped file and the $fileTypes. This is the if block if ($name -Match $fileTypes) and -Match is the RegEx parameter.

Next, we need to reset our $counter on every new folder. We do that with another if block which test if $dir is not equal (-nq). If it is true the $dir will get the current name and will reset the $counter.

We also want our counter to be in double digit format. With $zero we will cover this case. We are using ternary operator which test if $counter is less or equal to 9 and if true will be equal to string "0".

The next line is the actual file renaming. The Rename-Item is the command and in our case it takes $fullname which is the full path with the filename and the new name - "$zero$counter-$dir$extension". The new name is the combination of the counter with 2-digit format and the folder name with extension.

Finaly, we will increment $counter.

I tried to cover most of the common cases to have a good base from which to expand and improve to all your needs. You can run and change the script but always test it in a demo folder, because there is no undo button.

Final code

$path = "project\\test"
$fileTypes = ".*.jpg|.*.bmp|.*.png|.*.gif|.*.tif"
$files = Get-ChildItem -Recurse | Where-Object FullName -Match ".*$path*"
$counter = 1
$dir = ""

foreach ($file in $files) {
    $name = $file.Name
    $fullname = $file.FullName
    $extension = $file.Extension

    if ($name -Match $fileTypes) {
        if ($dir -ne $file.Directory.Name) {
            $dir = $file.Directory.Name
            $counter = 1
        }

        $zero = If ( $counter -le 9) { "0" } Else { "" }

        Rename-Item $fullname "$zero$counter-$dir$extension"

        $counter++
    }
}

Related articles

© 2021 All rights reserved.