SOLVED

Rename files to get the year, month and day of the creation in front of the file?

Copper Contributor

Hi all, I am a trainee and need to make some changes to the file naming convention.

There are tens of thousands of files in the filesystem and it would be a long day to do it all manually.

So my question is how to make it possible with a script to automate this process.

It would be nice to run the script into the start folder or select the start point and from there go downwards into all folders to get the name and creation date of a file that does not match the naming convention mentioned.

So that any file e.g. "logo.png" or "hello.world_2021.pdf" which was created on 05/05/2021 will look like "20210505_logo.png" and "05052021_hello.world_2021.pdf" afterwards.

And any file that contains the year, month and day at the beginning should be ignored.

 

Thanks for the help.

8 Replies

@pspBugs 

 

It isn't difficult to add a date-based prefix, but you've provided two different formats in your example below (yyyyMMdd_ and ddMMyyyy_.) Which is your preference?

 

And are you sure you want the creation date, not the last modified date?

 

Cheers,

Lain

best response confirmed by pspBugs (Copper Contributor)
Solution

@pspBugs 

 

Here's a three-line example on how you can do this. I've gone with the assumptions that:

 

  1. The prefix date format should be "yyyyMMdd_";
  2. You do indeed want the creation date used in the prefix, not the last modified date.
$ParsedDate = [datetime]::MinValue;
$Path = "D:\Data\SomePath";
Get-ChildItem -File -Path $Path -Recurse |
    Where-Object { (-not [regex]::IsMatch($_.Name, "^\d{8}_", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)) -or (-not [datetime]::TryParseExact($_.Name.Substring(0, 8), "yyyyMMdd", [cultureinfo]::CurrentCulture, [System.Globalization.DateTimeStyles]::None, [ref] $ParsedDate)) } |
        ForEach-Object { Rename-Item -Path ($_.FullName) -NewName "$($_.CreationTime.ToString("yyyyMMdd"))_$($_.Name)"; }

 

Cheers,

Lain

Hey that was fast. Thank you so much.
Yes the year musst be in front.
I will try it as soon as possible, but it looks great.

By the way where should I start with training for Powershell? Is there a course or a training book or something else?

pspBugs


EDIT:

I tested the code and it worked, but it did not take the creation date of each file every time.

So it takes the date from when the script was executed. Is there any way to loop this and get the individual creation date of each file?
And on repeated execution, the already renamed files are not ignored.

 

pspBugs

@pspBugs 

 

Are you sure you don't want the last modified date instead? Here's an example to illustrate why I ask.

 

Here's the sample files I tested against. Windows Explorer shows the date modified by default, not the creation date.

 

It's important to note I copied these files from a different location, meaning the originals have a different creation date. This diminishes the value of the creation time since it may not actually be the date the author created the file at all, but simply the last time it was copied.

 

LainRobertson_1-1652576459207.png

 

Just to be sure, let's have a look at the dates on the "input.settings" file in PowerShell, noting CreationTime in particular.

 

LainRobertson_0-1652576417862.png

 

Here, you can see that the CreationTime is today (the actual time I performed the file copy) and that it is actually greater than the LastWriteTime. Now, this is all well and good from the file system perspective where it makes sense, but it's probably counterintuitive to what you're hoping to achieve.

 

In short, the CreationTime is somewhat unreliable, whereas the LastWriteTime is preserved even during the any copy process. This is why I was wondering if you wouldn't prefer the last modified date.

 

The script itself is not using the current date through some indirect means (like, say, [datetime]::Now). It really is pulling the CreationTime from the file.

 

Cheers,

Lain

@pspBugs  

 

I just noticed your second comment about existing prefixes not being ignored.

 

I'm not seeing that reflected when I run it multiple times over the test files. It's putting the prefix in the first time then ignoring it on subsequent runs.

 

Maybe you can drop an example into this thread so we can get an idea of what's going on behind the scenes?

 

If you could drop in the following for one of the repeatedly-changing files, that might help:

 

  1. File name;
  2. File creation date;
  3. Which version of PowerShell you're running (I'm running under Windows PowerShell 5.1.)

 

You can get points 1 and 2 via:

 

Get-Item -Path ".\SomeFileName" | Select-Object -Property Name, CreationTime

 

 

You can get point 3 from the following:

 

$PSVersionTable

 

 

Cheers,

Lain

First of all sorry for the late answer.

I tried the script at home first and found this strange behavior I discribed above. But at work, and now at home, I could not reproduce it again.

To answer the question, here are the answers from Powershell for questions 1 to 3.

"PS C:\WINDOWS\system32> Get-Item -Path "C:\Users\user\Downloads\jhjk\Test\20220514_yyyyMMdd_rename.ps1" | Select-Object -Property Name, CreationTime


$PSVersionTable"

Name CreationTime
---- ------------
20220514_yyyyMMdd_rename.ps1 05/16/2022 21:04:34

Name Value
---- -----
PSVersion 5.1.19041.1645
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.1645
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1

So Thank you for the Help.

pspBugs

This is working perfectly. If I want to add the hours and minutes in 24hr to the prefix, like 202303271430 for a file created at 14:30 today. Would that be easy to work in? tried to just put hhmm after the tostring but that's actually putting it in as 202303270230. Must be something about 12 vs 24 hr format.
Looks like you have something smart in there to ignore files already with the prefix added.

@Keith80s 

 

Just be aware that the special format characters are case-sensitive. For example:

 

Format stringMeaning
hhTwo-digit hour using 12-hour clock.
HHTwo-digit hour using 24-hour clock.

 

Months (MM) and minutes (mm) is another one to be wary of.

 

You can find the full reference here:

 

 

Cheers,

Lain

1 best response

Accepted Solutions
best response confirmed by pspBugs (Copper Contributor)
Solution

@pspBugs 

 

Here's a three-line example on how you can do this. I've gone with the assumptions that:

 

  1. The prefix date format should be "yyyyMMdd_";
  2. You do indeed want the creation date used in the prefix, not the last modified date.
$ParsedDate = [datetime]::MinValue;
$Path = "D:\Data\SomePath";
Get-ChildItem -File -Path $Path -Recurse |
    Where-Object { (-not [regex]::IsMatch($_.Name, "^\d{8}_", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)) -or (-not [datetime]::TryParseExact($_.Name.Substring(0, 8), "yyyyMMdd", [cultureinfo]::CurrentCulture, [System.Globalization.DateTimeStyles]::None, [ref] $ParsedDate)) } |
        ForEach-Object { Rename-Item -Path ($_.FullName) -NewName "$($_.CreationTime.ToString("yyyyMMdd"))_$($_.Name)"; }

 

Cheers,

Lain

View solution in original post