Whats the powershell command for finding files in a folder that do not meet a criteria of date range for the Get-Childitem filter of LastWriteTime.
So, check to see if a directory has files that DO NOT contain any files that have LastWriteTime between 01/10/2012 (1st Oct) to 25/10/2012 (25th Oct).
I want to display the folders that DO NOT have any files that are in that range...that way I know they are old and the whole directory can be deleted.
example of this is:
Folder1 - some files written within October - ignore this whole folder and do not display these in the results
Folder2 - NO file has LastWriteTime written in the month of October and so this folder and files should be displayed.
I know this can be done with Get-ChildItem and I am stuck on the bit below..
Get-ChildItem E:\LogArchive -Recurse | Where-Object{$_.LastWriteTime.......?
Simple solution off top of the head is this:
Where-Object{ $_.LastWriteTime -lt $startDate -or $_.LastWriteTime -gt $endDate }
where $startDate is 01/10/2012 (1st Oct) and $endDate is 25/10/2012 (25th Oct).
Problem with this approach is that it does not account for time factor in the [datetime]. So in your example, if you have 25-Oct-2012 as an upper bound, it will return files created at 25-Oct-2012 9:00, which you may not want.
Below code calculates [datetime]'s with time part truncated, based on the input [datetime], and builds the month-to-date date range of ($dayStart, $dayEnd):
$entry_date = "20-Oct-2012 9:00" #to feed current date, use this: Get-Date
$dayEnd = Get-Date $entry_date -Minute 0 -Hour 0 -Second -0;
$dayStart = Get-Date $endDate -Day 1;
$dayStart, $dayEnd
For this sample code, here is the output:
Monday, October 01, 2012 12:00:00 AM
Saturday, October 20, 2012 12:00:00 AM
Notice this approach is flexible, because you can set $entry_date to either Date or String, and it may or may not have time part - it will work in all those cases. You can then have this code in Where-Object:
Where-Object{ $_.LastWriteTime -lt $dayStart -or $_.LastWriteTime -ge $dayEnd.AddDays(1) }
Notice how -ge $dayEnd.AddDays(1) fixes the issue when comparing 25-Oct-2012 to 25-Oct-2012 9:00.
Try this:
[datetime]$startDate = "10/01/2012" # or $startDate = get-date -Date 1/10/2012
[datetime]$endDate = "10/25/2012" # or $endDate = get-date -Date 25/10/2012
? { $_.LastWriteTime -lt $startDate -or $_.LastWriteTime -gt $endDate }
Related
I need to filter only the files which are modified in last 20 mins. Below is the code I have written.
$Today = (Get-Date).AddMinutes(-20).ToString("MM/dd/yyyy hh:mm tt")
$filecreation = Get-ChildItem "D:\logs\" -Recurse
Foreach($file in $filecreation)
{
if ($file.LastWriteTime.ToString("MM/dd/yyyy hh:mm tt") -gt $Today)
{
Write-Host $file.LastWriteTime.ToString("MM/dd/yyyy hh:mm tt")
}
}
Output is below:
08/26/2020 07:39 PM
08/26/2020 07:54 PM
08/26/2020 10:58 AM
08/26/2020 07:54 PM
08/26/2020 12:01 AM
08/26/2020 12:01 AM
08/26/2020 12:01 AM
Problem:
So you see, I have got the files written in morning as well. I want to compare the AM and PM as well. Can somebody please help me with this.
Also, the I have multiple date folders inside "D:\logs". And the same are also captured in the output. I just want to compare the file and copy. How can I ignore the folders in Get-ChildItem?
Please help.
Regards,
Mitesh Agrawal
Stop turning [datetime] objects into strings for comparison!
The [datetime] type already supports three-way comparisons, so comparing string representations of existing [datetime] values is completely unnecessary - and unless you're using a strictly endian date format (like yyyyMMddHHmmss) it also won't work as intended - simply because the rules for sorting something alphabetically are different than sorting according to some description of time.
# $Today is now a [datetime] value, not a string
$Today = (Get-Date).AddMinutes(-20)
$filecreation = Get-ChildItem "D:\logs\" -Recurse
Foreach($file in $filecreation)
{
# Now we just compare them directly as-is
if ($file.LastWriteTime -gt $Today)
{
Write-Host $file.LastWriteTime.ToString("MM/dd/yyyy hh:mm tt")
}
}
Use !D Attribute to search files and not directories
You can use the following code. This eliminates the sub-directories(by using !Directory or !D) search and use 24 hr format to get the final files with added AM and PM.
$Today = (Get-Date).AddMinutes(-20).ToString("MM/dd/yyyy HH:mm tt")
$filecreation = Get-ChildItem -Attributes !Directory -Path "D:\logs\"
Foreach($file in $filecreation)
{
if ($file.LastWriteTime.ToString("MM/dd/yyyy HH:mm tt") -gt $Today)
{
Write-Host $file.LastWriteTime.ToString("MM/dd/yyyy HH:mm tt")
}
}
Recurse is not necessary until and unless if the subdirectory files needs to be compared
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 $_)
}
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!
$timeLimit1 = Get-Date.AddDays(-90)
Get-ChildItem <path> | ? {$_.LastWriteTime -le $timeLimit1 } | Remove-Item
So I have the easier part, but I am trying to figure out how to do something a little more complex.
I am setting up a backup deletion routine. The requirement is to keep the last 90 days of backups, then the final day of each month prior to that for the current year and finally a backup from December 31st for the prior 2 years.
I couldn't find any examples other than just a single check; is it possible to do several checks to automate that.
I suggest writing a custom filter to apply the logic e.g.
filter MyCustomFilter{
$File = $_
$FileDate = $File.LastWriteTime
$Now = (Get-Date)
$FileAgeDays = ($Now - $FileDate).TotalDays
if (
# Keep files for at least 90 days
( $FileAgeDays -lt 91
) -or
# Keep files from last day of the month this year
( $FileDate.Year -eq $Now.Year -and
$FileDate.Month -ne $FileDate.AddDays(1).Month
) -or
# Keep files from 31 Dec for up to 2 years
( $FileAgeDays -lt ( 2 * 365 ) -and
$FileDate.Month -eq 12 -and
$FileDate.Day -eq 31
)
) {
# File should be kept, so nothing is returned by the filter
} else {
# File should be deleted, so pass the file down the pipeline
write-output $File
}
}
Now your overall code would look something like this:
get-childItem -path <path> |
where-object { -not $_.PSIsContainer } |
MyCustomFilter |
Remove-Item
It goes without saying, that proper testing is required before letting this loose on production systems.
EDIT: A Simpler test for last day of month
I thought of a neater 'last day of the month' test, which is simply to compare the month property of the date under test, with the month property of the following day e.g.
$FileDate.Month -ne $FileDate.AddDays(1).Month
will return $true if $FileDate is last day of the month.
Just create an array of your "special" dates and add them to your filter.
$timeLimit1 = Get-Date
$timeLimit1.AddDays(-90)
$specialDates = (Get-Date "31-12-2014"), (Get-Date "31-05-2017") # continue the list
Get-ChildItem <path> | ? {$_.LastWriteTime -le $timeLimit1 -and $specialDates -notcontains $_.LastAccessTime.Date } | Remove-Item
I would also make a function to find the "special" dates.
I have a folder structure in a directory called FTP.
\\FTP\December 15
\\FTP\January 15
\\FTP\February 15
\\FTP\March 15
etc...
I will be moving files to this folder using a powershell script: multiple files. potentially multiple folders.
I want to extract the month
$month = get-date -format m
This will return December
Now how do I write the GCI statement to match the folder to the month
For example: Something like this?
gci '\\FTP\' -recurse WHERE $_.Fullname -like $a.substring(0,9)
Please help
Here is a function to get the month specific folder then you can do whatever you need by replacing the "Do Work To Said Folder" Comment with whatever you want to do to the folder.(Copy To, Get-ChildItems, etc.)
function Get-MonthlyFolder
{
$month = get-date -format m;
#Bellow Line Gets The Full Path To Current Monthly Folder Stored In $folderInst variable:
$folderInst = gci "\\FTP" | Where {$.Name -like "$month*"} | Select FullName;
#Do Work To Said Folder
}
Get-MonthlyFolder;
$month = get-date -format m returns December 15 I assume that's correct.
Then your code will look like this:
Get-ChildItem \\FTP\ -recurse | where { $_.Fullname -like "*$month*"}
we use Where-Object which will return each item matching the expression.