Powershell Script to create folder by week number for 2022 - powershell

I am trying to create a folder by week number so far i am able to create it for the current week however i would like to create it for the entire year i.e. 2022 under the respective months i.e. March April etc.. All the week number folders should be created inside these folders.
$currentweek = "{0:d1}" -f ($(Get-Culture).Calendar.GetWeekOfYear((Get-Date),[System.Globalization.CalendarWeekRule]::FirstFourDayWeek, [DayOfWeek]::Monday))
$destFolder = New-Item -Path C:\C:\Users\MAdmin\Documents\VA05\2022\March\Week_$currentweek -Type Directory -ErrorAction SilentlyContinue ```

If I understand correctly, you would like to create a folder tree of week numbers in the year 2022, because as you have commented in reply of Tomalak's comment, creating these weeknumber subfolders inside Monthname folders can hardly be done correctly since weeknumbers can overlap months.
To do this, I would use a helper function to obtain the first Thursday of the first week in the year. (ISO 8601 dictates that week 1 starts with the week that has a Thursday in it):
function Get-FirstThursday {
param(
[Parameter(Mandatory = $false, Position = 0)]
[int]$Year = [DateTime]::Now.Year,
[Parameter(Mandatory = $false, Position = 1)]
[int]$WeekNumber = [CultureInfo]::InvariantCulture.Calendar.GetWeekOfYear([DateTime]::Now,
[System.Globalization.CalendarWeekRule]::FirstFourDayWeek,
[DayOfWeek]::Monday)
)
$january1 = [DateTime]::new($Year,1,1,0,0,0,[System.DateTimeKind]::Local)
$offset = [int]([DayOfWeek]::Thursday - $january1.DayOfWeek)
$thursday1 = $january1.AddDays($offset)
# ISO 8601
$week1 = [CultureInfo]::InvariantCulture.Calendar.GetWeekOfYear($thursday1,
[System.Globalization.CalendarWeekRule]::FirstFourDayWeek,
[DayOfWeek]::Monday)
if($week1 -le 1) { $WeekNumber -= 1 }
# return the date for the first Thursday of the week in the given year
$thursday1.AddDays($WeekNumber * 7)
}
Having that function on top of the script, you can create the folders like:
$destination = 'C:\Users\MAdmin\Documents\VA05'
$year = (Get-Date).Year # 2022
# get the first Thursday of week 1 in the given year
$thursday = Get-FirstThursday -Year $year -WeekNumber 1
# keep adding 7 days to this first Thursday until we reach next year
# create weeknumber subfolders (most years 52, some years 53)
$week = 0
while ($thursday.Year -eq $year) {
$thursday = $thursday.AddDays(7)
$week++
# create a subfolder for this weeknumber
$targetFolder = Join-Path -Path $destination -ChildPath ('{0}\{1:D2}' -f $year, $week)
$null = New-Item -Path $targetFolder -ItemType Directory -Force
}

Related

How to build path and file name for excel sheet from SMB share

I have below share and folders. For every month there will be a folder and there will be year folder prior to that as shown
Last Friday of every month there is excel sheet generated as shown below
End piece in file name "-14-26-48" remain same but prior pieces changes as per year and date. I would like to get this excel sheet path and name build in PowerShell. So how can i code so i get following path without manually entering complete path and filename. whenever i run new code i should get path and file name for last sheet which was generated last Friday of last month.
$oldbk = Import-Excel -Path '\\hcohesity05\cohesity_reports\2022\7\07-29\Cohesity_FETB_Report-2022-07-29-14-26-48.xlsx'
If you just want the file from the last Friday of the previous month the easiest way to find it is not to build the path, but just search a month's folders and filter for files created on a Friday, sort by time created, and get the first one (once sorted the last folder created will be the first item).
$LastMonth = [datetime]::Now.AddMonths(-1).ToString('yyyy\\M')
$TargetFolder = Get-ChildItem (Join-Path '\\hcohesity05\cohesity_reports' $LastMonth) |Where{$_.CreationTime.DayOfWeek -eq 'Friday'}|Sort CreationTime|Select -first 1
$FilePath = (Resolve-Path (Join-Path $TargetFolder.FullName 'Cohesity_FETB_Report-*-14-26-48.xlsx')).Path
$oldbk = Import-Excel -Path $FilePath
You can use the [DateTime] type to help you out here.
# To do this, we create a new datetime,
# (using the current month and year, but the first day).
$endOfMonth = [datetime]::new(
[DateTime]::Now.Year,
[DateTime]::Now.Month,
1
).AddMonths(1).AddDays(-1)
# Then we add a month to it and subtract a day
# (thus giving us the last day of the month)
# Now we create a variable for the last Friday
$lastFriday = $endOfMonth
# and we keep moving backward thru the calendar until it's right.
while ($lastFriday.DayOfWeek -ne 'Friday') {
$lastFriday = $lastFriday.AddDays(-1)
}
# At this point, we can make the ReportPath, piece by piece
# ($BasePath would be the path until this point)
$ReportPrefix = "Cohesity_FETB_Report"
$ReportSuffix = "14-26-48.xlsx"
$ReportPath = Join-Path $BasePath $lastFriday.Year |
Join-Path -ChildPath $lastFriday.Month |
# A Custom format string will be needed for the month/day
Join-Path -ChildPath $lastFriday.ToString("MM-dd") |
Join-Path -ChildPath "$reportPrefix-$($lastFriday.ToString('YYYY-MM-dd'))-$reportSuffix"
$reportPath

Creating multiple folders with dates etc. 190101 to 191231

I'm trying to create multiple folders but for dates in a year.
Also, I have been using PowerShell and trying to create a batch script.
I had tried several solutions, but no one of them gives me what I need.
So, I need to create folders as 190101 to 191231 empty once for all year. But whatever I do I don't get what I want.
Examples:
01..31 | foreach $_{ New-Item -ItemType Directory -Name $("1901" + $_)}
mkdir $(01..31 | %{"ch$_"})
md(01..31|%{"1901$_"})
But the problem here they don't give me 0 in "days" so, I have
19011 instead of 190101.
I can't find how to extract dates and push PowerShell to create what I need.
here is a slightly more generalized version that will work for any given month. the -f string format operator is really quite handy ... [grin]
$Today = (Get-Date).Date
$Year = $Today.Year
$Month = $Today.Month
$DaysInMonth = (Get-Culture).Calendar.GetDaysInMonth($Year, $Month)
foreach ($Day in 1..$DaysInMonth)
{
'{0}{1:D2}' -f $Today.ToString('yyMM'), $Day
}
truncated output ...
190101
190102
[*...snip...*]
190130
190131
One way to create folders for every day of the year
define/get the year
set start date to Jan 1st
to have a zero based offset get the DayOfYear for the 30th December
use a range to AddDays to startdate and iterate
$year = (Get-Date).Year
$startdate = Get-Date -Year $year -Month 1 -Day 1
0..(Get-Date -Year $year -Month 12 -Day 30).DayOfYear| ForEach-Object{
mkdir ($startdate.AddDays($_).ToString('yyMMdd')
)
Use the format operator (-f). It was made for this exact purpose.
1..31 | ForEach-Object {
New-Item -Type Directory -Name ('1901{0:d2}' -f $_)
}

datetime comparison letting thread proceed when shouldn't

I have a backup script that puts files in a dated directory every night. I have another script, below, that goes through a list of dated directories and if it's within the range I want to delete, I will delete the directory, keeping the Saturday dated file. For some reason, for the dir dated Saturday, 1/12/2019, it's being deleted, even though the if statement should indicate it wouldn't be deleted.
This is my code:
function WeeklyCleanup($folderWeeklyCleanupDatedSubdirs) {
#find out date range to cleanup
$lastSaturday = GetLastSaturdayDate
$lastSaturday = [datetime]$lastSaturday
$weekAgoSunday = GetWeekAgoSundayDate
$weekAgoSunday = [datetime]$weekAgoSunday
#find out filename with Saturday date before current date but within week
Get-ChildItem $folderWeeklyCleanupDatedSubdirs | ForEach-Object {
write-output $_
#check to see if item is before day we want to remove
$temp = $_
$regex = [regex]::Matches($temp,'([0-9]+)-([0-9]+)-([0-9]+)')
if($regex.length -gt 0) #it matched
{
$year = $regex[0].Groups[1].Value
$month = $regex[0].Groups[2].Value
$day = $regex[0].Groups[3].Value
write-output $year
write-output $month
write-output $day
write-output "*************"
$dirDate = $regex[0].Groups[0].Value + " 12:00:00 PM"
write-output $dirDate
if($dirDate.length -gt 0) #it matched
{
$dateTimeObjectFromRegex = [datetime]$dirDate
##########this next statement is letting $dateTimeObjectFromRegex of 1/12/2019 through when it shouldn't. See time comparison below
if(([datetime]$dateTimeObjectFromRegex -lt [datetime]$lastSaturday) -and ([datetime]$dateTimeObjectFromRegex -ge [datetime]$weekAgoSunday)) #we're removing extra ones over last week, keep Saturday
{
$dirPathToRemove = Join-Path -path $folderWeeklyCleanupDatedSubdirs -ChildPath $temp.ToString()
Get-ChildItem -Path $dirPathToRemove #list the dir
#remove dir
if(-Not (Test-Path $dirPathToRemove )) #-PathType Container
{
$global:ErrorStrings.Add("Exception: No such path, $dirPathToRemove;; ")
write-output "++ Error: An error occured during copy operation. No such path, $dirPathToList ++"
}
else
{
#remove dir and subdirs
Remove-Item $dirPathToRemove -Force -Recurse
Get-ChildItem -Path $dirPathToRemove #list the dir
}
#Write-Output $_
#Write-Output " 1 "
} #if within last week
} #if dirDate length
} #if regex matched
} #get-childItem
}
function GetLastSaturdayDate()
{
$date = Get-Date #"$((Get-Date).ToString('yyyy-MM-dd'))"
#for($i=1; $i -le 7; $i++){
# if($date.AddDays(-$i).DayOfWeek -eq 'Saturday') #if found Saturday
# {
# $date.AddDays(-$i)
# $newDate = $date.AddDays(-$i)
# break
# }
#}
$newdate = $date.AddDays(-($date.DayOfWeek+1)%7)
return $newdate
}
function GetWeekAgoSundayDate()
{
$numberOfWeeks = 1; #week ago
$date = Get-Date #"$((Get-Date).ToString('yyyy-MM-dd'))"
#for($i=1; $i -le 7; $i++){
# if(($date.AddDays(-$i).DayOfWeek -eq 'Sunday') -and ($date.AddDays(-$i) -ne $date)) #if found a Sunday and it's not today
# {
# $date.AddDays(-$i)
# $newDate = $date.AddDays(-$i)
# break
# }
#}
#$newdate = $date.AddDays(-($date.DayOfWeek+1)%0)
$numDaysSincePreviousDate = $date.DayOfWeek.value__ + 0 #0 is Sunday
([System.DateTime] $previousDayOfWeek = $date.AddDays(- $numDaysSincePreviousDate)) | Out-Null
$previousDate = $previousDayOfWeek.AddDays(-($numberOfWeeks *7)).ToString("MM-dd-yyyy")
return $previousDate
}
The WeeklyCleanup script is called with this parameter:
$folderToCleanupDatedSubdirs = [System.IO.DirectoryInfo]"E:\Bak_TestDatedFolderCleanup"
For time comparison:
Directory item toLocRobo_2019-01-12 - Once I get regex with timestamp, I add the time in of 12:00:00 PM for the $dirDate variable. This becomes $dateTimeObjectFromRegex. Debugger shows it as Saturday, January 12, 2019 12:00:00 PM
When I run the program, I get $lastSaturday as Saturday, January 12, 2019 3:59:04 PM
When I run the program, debugger also shows $weekAgoSunday as Sunday, January 6, 2019 12:00:00 AM
From what I can see, it shouldn't be getting through that if statement to delete the dir for 1/12/2019.
I tried casting the dates to datetime to make sure it wasn't doing a string comparison in the if statement, even though I casted it above.
I've been looking at these links for more info on this, but it looks correct to me:
dateTimeComparisons not working expectedly
convert string to datetime
All I can think is that it's still treating the datetime in the first part of the if statement comparison like a string, so it's thinking 3:59 PM is greater than 12:00 PM. Any ideas how to get this date check to work? I don't care about times, just want to make sure it doesn't get rid of the Saturday-dated file from this past week, but only cleanup other dir's over that week.
Update
Made changes suggested by #brianist below, and it worked great. This is what it looks like so far. Saturday and Sunday functions haven't changed. He's asking me to post it. If he has any other suggestions how to get it to treat/compare what's returned from the last Saturday and weekAgoSunday functions as dates, without the cast, that'd make it look less clunky/easier to read. Thanks Brian!
#do weekly cleanup of DisasterBackup folder
function WeeklyCleanup($folderWeeklyCleanupDatedSubdirs) {
#find out current date
$currentDate = "$((Get-Date).ToString('yyyy-MM-dd'))" #example 2019-01-15
$currentDayOfWeek = (get-date).DayOfWeek.value__ #returns int value for day of week
#find out current day of week
$lastSaturday = GetLastSaturdayDate
$lastSaturday = [datetime]$lastSaturday #if we take away extra casts it won't do comparison line (-lt and -ge)
$weekAgoSunday = GetWeekAgoSundayDate
$weekAgoSunday = [datetime]$weekAgoSunday #if we take away extra casts it won't do comparison line (-lt and -ge), and can't move cast before function call because it isn't recognizing function anymore if I do
#find out filename with Saturday date before current date but within week
#get each dir item to check if we need to remove it
Get-ChildItem $folderWeeklyCleanupDatedSubdirs | ForEach-Object {
write-output $_
#check to see if item is before day we want to remove
$temp = $_
if($_.Name -match '(\d{4}-\d{2}-\d{2})$'){ #regex
write-output $Matches[1]
$dirDate = Get-Date $Matches[1] #turn regex into date
if(([datetime]$dirDate.Date -lt [datetime]$lastSaturday.Date) -and ([datetime]$dirDate.Date -ge [datetime]$weekAgoSunday.Date)) #we're removing extra ones over last week, keep Saturday
{
$dirPathToRemove = Join-Path -path $folderWeeklyCleanupDatedSubdirs -ChildPath $temp.ToString()
Get-ChildItem -Path $dirPathToRemove #list the dir
#remove dir
if(-Not (Test-Path $dirPathToRemove )) #-PathType Container
{
$global:ErrorStrings.Add("Exception: No such path, $dirPathToRemove;; ")
write-output "++ Error: An error occured during copy operation. No such path, $dirPathToList ++"
}
else
{
#remove dir and subdirs
Remove-Item $dirPathToRemove -Force -Recurse
Get-ChildItem -Path $dirPathToRemove #list the dir
}
} #if within last week
} #if regex matched
} #get-childItem
}
Re-reading your last sentence, I now see that your intention is to ignore times, not to include them in your comparison.
You are right to want to use [DateTime] objects when comparing, but do note that those objects always include a time.
Conveniently you can use the .Date property of such an object which returns a new one, with the time set to midnight. This is useful for comparing because the time would no longer be a factor.
Pulling out my modified if statement from below, you can do it like this:
if ($dirDate.Date -lt $lastSaturday.Date -and $dirDate.Date -ge $weekAgoSunday.Date) {
# do stuff
}
Now you're only comparing dates, and ignoring time!
Based on what you're showing it looks like the if statement is working as expected. To summarize, you say that:
$dateTimeObjectFromRegex is Saturday, January 12, 2019 12:00:00 PM
$lastSaturday is Saturday, January 12, 2019 3:59:04 PM
$weekAgoSunday is Sunday, January 6, 2019 12:00:00 AM
The conditional is:
if(([datetime]$dateTimeObjectFromRegex -lt [datetime]$lastSaturday)
-and ([datetime]$dateTimeObjectFromRegex -ge [datetime]$weekAgoSunday))
Therefore in pseudo-code it's:
if (
("January 12, 2019 12:00:00 PM" is earlier than "January 12, 2019 3:59:04 PM") # true
and
("January 12, 2019 12:00:00 PM" is later or the same as "January 6, 2019 12:00:00 AM") # true
) # true
I do want to point out that you are doing an awful lot of unnecessary datetime chopping and casting, and cleaning that up would help with readability and with debugging these types of issues.
$lastSaturday = GetLastSaturdayDate
$lastSaturday = [datetime]$lastSaturday
$weekAgoSunday = GetWeekAgoSundayDate
$weekAgoSunday = [datetime]$weekAgoSunday
Your functions already return [DateTime] objects, so there's no need for those casts.
$temp = $_
$regex = [regex]::Matches($temp,'([0-9]+)-([0-9]+)-([0-9]+)')
if($regex.length -gt 0) #it matched
{
$year = $regex[0].Groups[1].Value
$month = $regex[0].Groups[2].Value
$day = $regex[0].Groups[3].Value
write-output $year
write-output $month
write-output $day
write-output "*************"
$dirDate = $regex[0].Groups[0].Value + " 12:00:00 PM"
write-output $dirDate
This is quite complex, you could simplify it down to something like this:
if ($_.Name -match '(\d{4}-\d{2}-\d{2})$') {
# in here, you know the match was successful
$dirDate = Get-Date $Matches[1] # double check this, might need .Groups or something
if ($dirDate -lt $lastSaturday -and $dirDate -ge $weekAgoSunday) {
# do stuff
}
}
Probably some more optimizations etc. Hope this was helpful!

How to compare to string in PowerShell foreach loop to files

How to compare the current month with file modified in current month using power shell script. My code is working file but it is reading all the csv file in the given directory. I just wanted to read current month file i.e. modified in October 2018.
Please help me out , I have total 6 files in my directory. 3 files having date modified in October 2018 and remaining 3 files are modified in September 2018.
I want my script to check the current month then read all csv of current month i.e. October 2018
Code:
$files = Get-ChildItem 'C:\Users\212515181\Desktop\Logsheet\*.csv'
$targetPath = 'C:\Users\212515181\Desktop\Logsheet'
$result = "$targetPath\Final.csv"
$curr_month = (Get-Date).Month
$curr_year = (Get-Date).Year
# Adding header to output file
[System.IO.File]::WriteAllLines($result,[System.IO.File]::ReadAllLines($files[1])[1])
foreach ($file in $files)
{
$month = $file.LastWriteTime.ToString()
$curr_month=(Get-Date).Month
if ($month= $curr_month)
{
$firstLine = [System.IO.File]::ReadAllLines($file) | Select-Object -first 1
[System.IO.File]::AppendAllText($result, ($firstLine | Out-String))
$lines = [System.IO.File]::ReadAllLines($file)
[System.IO.File]::AppendAllText($result, ($lines[2..$lines.Length] | Out-String))
}
}
# Change output file name to reflect month and year in MMYYYY format
Rename-Item $result "Final_$curr_month$curr_year.csv"
Your comparison is wrong. And will return $true causing all files to be read
It should be
if ($month -eq $curr_month)
Also I would remove the second
$curr_month = (get-date).month
it's adding overhead to your script as you set it before the loop

Remove certain folders recursively?

I've a folder structure that looks like this
-2018
--01-Jan
--02-Feb
--etc
-2017
--01-Jan
--02-Feb
--etc
Is there a way to delete all directories that are over 7 years old (according to this naming structure ... not according to Creation / Modified Date etc)?
So if I ran it in August 2018 I would be left with
-2018
-2017
-2016
-2015
-2014
-2013
-2012
-2011
--08-Aug
--09-Sep
--10-Oct
--11-Nov
--12-Dec
So the 2012 - 2018 folders would remain untouched.
Any folder 2010 and earlier would be deleted.
And any folder within 2011 '07-Jul' or smaller would be deleted.
Thanks
P
I first created a comparable folder structure using this code:
##
## define enum for months
##
enum month {
Jan = 1
Feb = 2
Mar = 3
Apr = 4
May = 5
Jun = 6
Jul = 7
Aug = 8
Sep = 9
Oct = 10
Nov = 11
Dec = 12
}
##
## create folder structure
##
New-Item -Path c:\ -Name Testdata -ItemType Directory
2018..2005 |
foreach {
New-Item -Path c:\Testdata -Name $psitem -ItemType Directory
$path = "c:\Testdata\$psitem"
1..12 |
foreach {
$name = "{0:00}-{1}" -f $psitem, [month]$psitem
New-Item -Path $path -Name $name -ItemType Directory
}
}
That gives me an easy structure to test. I'm assuming that your year folders are subfolders of something. If they are in the root of a drive that works as well.
To delete the folders:
enum month {
Jan = 1
Feb = 2
Mar = 3
Apr = 4
May = 5
Jun = 6
Jul = 7
Aug = 8
Sep = 9
Oct = 10
Nov = 11
Dec = 12
}
$date = Get-Date
$year = $date.Year - 8
##
## delete evreything 8 years or older
##
Get-ChildItem -Path C:\Testdata -Directory |
where Name -le $year |
foreach {
Remove-Item -Path $psitem.Fullname -Recurse -Force -Confirm:$false
}
##
## if Month -ne January
## need to delete some months
##
if ($date.Month -gt 1){
$path = "C:\testdata\$($year+1)"
$month = $date.Month -1
1..$month |
foreach {
$mpath = "$path\{0:00}-{1}" -f $psitem, [month]$psitem
Remove-Item -Path $mpath -Recurse -Force -Confirm:$false
}
}
I'm making an assumption about the three letter abbreviations you use but you can easily change the enum.
The code gets the current date and gets the year minus 8. It loops through your top level folder and gets the folders that are less than or equal to the year you've defined. They, and their contents, are force deleted. Only thing that could stop the deletion is if you have one of the files pinned open.
If the current month is January there's nothing else to do. Otherwise create the path to the -7 year folder and calculate the last month you want to delete. Loop through the months, build the path and force the deletion of the folder and its contents.
The bulk of the work is done at the year level with a quick clean up of the months. I'd suggest testing against a number of months to check the logic is what you need.
Okay, this is very simple and it will require you to do some coding and using a nested loop essentially. What you need to understand is how to use the Get-Date verb. So a sample that will remove all data recursively before 2011 would look like this
# Set your Path
$MyFolderPath = "C:\MyPath"
# Define the object to hold the folders (We are only looking for Folders not files)
$folders = Get-Childitem -Path $MyFolderPath -Directory
# Loop through each folder
foreach($folder in $folders)
{
# Using a cast to integer compare the Year using the folder name with the
# Get-Date function -7 years from this year
if([int]$folder.Name -lt (Get-Date).AddYears(-7).Year)
{
# Now remove the entire folder recursively without prompting.
Remove-Item $folder.FullName -Recurse -Force -Confirm:$false
}
}
Now to achieve it to the level of the month. I will let you play around with a nested loop and get through to that level. I hope this helps you...