Moving files older than X days not working in Powershell - powershell

I have the following code:
$dt = Get-Date -format "MM-dd-yyyy"
$logFolder = '\\192.168.20.151\user_backups\Veeam_Logs'
$source = "$logFolder\*.log"; $destination = "$logFolder\Backup Logs [$dt]"
New-Item -ItemType Directory -Path $destination -Force
Move-Item ($source | Where LastWriteTime -gt (Get-Date).AddDays(-7)) $destination -Force
I'm trying to move *.log files in its current directory (so not folders & not recursive) to its sub-folder but only those log files that are older than 7 days. For some reason the above isn't working as it's still copying files that are 20 days old. There's no error.
Also I don't want it to give me any errors if there are no *.log files to copy (or at least when there's no match). -ErrorAction SilentlyContinue didn't work for some reason.
Your help is appreciated.

To perform a condition against LastWriteTime attribute, you need to first return a FileInfoObject. A path string passed into Move-Item -Path won't have the attribute. You can do something like the following:
$dt = Get-Date -format "MM-dd-yyyy"
$logFolder = '\\192.168.20.151\user_backups\Veeam_Logs'
$destination = "$logFolder\Backup Logs [$dt]"
$source = Get-ChildItem -Path "$logFolder\*.log" | Where LastWriteTime -gt (Get-Date).AddDays(-7)
New-Item -ItemType Directory -Path $destination -Force
Move-Item -LiteralPath $source -Destination $destination -Force
$source here performs all the conditional logic. It is unclear if you want files newer or older than a certain date. If you want files that are newer than $date, you will want to use LastWriteTime -gt $date. If you want files older than $date, you will want to use LastWriteTime -lt $date.
You can have -Path value perform the logic, but you must pass in an expression.
$dt = Get-Date -format "MM-dd-yyyy"
$logFolder = '\\192.168.20.151\user_backups\Veeam_Logs'
$destination = "$logFolder\Backup Logs [$dt]"
$source = Get-ChildItem -Path "$logFolder\*.log"
New-Item -ItemType Directory -Path $destination -Force
Move-Item -LiteralPath ($source | Where LastWriteTime -gt (Get-Date).AddDays(-7)) -Destination $destination -Force

Related

How to group files by year and months (20200819) and archive at one location using PowerShell script

I have thousands of files of many years and I want to archive these files on year -> month basis. I want to keep latest 2 months of files and older than 2 months should be archived. The catch is to determine the year and month of a specific file I have to do it from the file name.
Filename format : ABCXYZ_20220715.xml
First 4 digits are year (2022), followed by 2 digits of month(07) and 2 digits of day(15).
These files not necessarily were created on the same date as given in the file name. Otherwise it would have been easy for me to achieve this using group by $_.LastWriteTime
Below is the desired output:
Script I wrote to achieve this, but using $_.LastWriteTime and NOT from the file name.
# Two months from the beginning of the month
$today = [datetime]::Today
$maxAge = $today.addMonths(-2)
$SourceFolder = "C:\Temp\sent"
$DestinationFolder = "C:\Temp\Archive"
$filesByMonth = Get-ChildItem -Path $SourceFolder -File |`
where LastWriteTime -LT $maxAge |`
group { $_.LastWriteTime.ToString("yyyy\\MM") }
foreach ($monthlyGroup in $filesByMonth) {
$archiveFolder = Join-Path $DestinationFolder $monthlyGroup.Name
New-Item -Path $archiveFolder -ItemType Directory -Force
$monthlyGroup.Group | Move-Item -Destination $archiveFolder
# $monthlyGroup.Group | Move-Item -Destination $_.fullName.Replace($SourceFolder, $archiveFolder)
#the second $archivefolder is the name for the ZIP file, the extensions is added automatically
Compress-Archive -Path $archiveFolder -DestinationPath $archiveFolder
Remove-Item $archiveFolder -Recurse
}
One solution is the following modification of your code. It just adds the custom "GroupableNameDate" with the date extracted from the files.
# Two months from the beginning of the month
$today = [datetime]::Today
$maxAge = $today.addMonths(-2)
$SourceFolder = "C:\Temp\sent"
$DestinationFolder = "C:\Temp\Archive"
$filesByMonth = Get-ChildItem -Path $SourceFolder -File |`
where LastWriteTime -LT $maxAge |`
Select *,#{
Name="GroupableNameDate"
Expression={
$d = $_.basename -split "_" | Select-Object -Last 1
[System.DateTime]::ParseExact($d,'yyyyMMdd',$null)
}
} |
group { $_.GroupableNameDate.ToString("yyyy\\MM") }
foreach ($monthlyGroup in $filesByMonth) {
$archiveFolder = Join-Path $DestinationFolder $monthlyGroup.Name
New-Item -Path $archiveFolder -ItemType Directory -Force
$monthlyGroup.Group | Move-Item -Destination $archiveFolder
# $monthlyGroup.Group | Move-Item -Destination $_.fullName.Replace($SourceFolder, $archiveFolder)
#the second $archivefolder is the name for the ZIP file, the extensions is added automatically
Compress-Archive -Path $archiveFolder -DestinationPath $archiveFolder
Remove-Item $archiveFolder -Recurse
}

PowerShell - sort&move files by Edit date

Simple task. But can't realize.
I just need to take file from one directory (x) and move to another (z) + create there folder named "YYYY" (year of last edit), then, subfolder named "MM" (month of last edit).
So i find script and tried to adapt it for my needs. But, i can't run it succsesfully.
Here is code witch i took as example:
$files= Get-ChildItem -File «C:\Files\»
foreach ($file in $files) {
if ($file.lastwritetime -lt $lastweek) {
$file | Move-Item -Force -Destination { md («C:\Files\» + $_.LastWriteTime.ToString(«yyyy.MM») + «\» + $_.LastWriteTime.ToString(«yyyy.MM.dd»)) -Force}
}
}
So i made mine based on it:
$files= Get-childitem -path "c:\Files"
$files | foreach-object {Move-Item -Force -Destination { md ("C:\Files\" + $_.LastWriteTime.ToString("yyyy")+"\"+$_.LastWriteTime.ToString("MM"))}}
Please help me make it work!
Im not really good with powershell...
Better not construct paths by concatenating strings with +. To avoid errors, use the Join-Path cmdlet for that.
Also I would advice not trying to cram that much in one line of code, because when it fails, it is really hard to debug.
Try
$path = 'C:\Files'
Get-childitem -Path $path -File |
ForEach-Object {
$destination = Join-Path -Path $path -ChildPath ('{0}\{1:D2}' -f $_.LastWriteTime.Year, $_.LastWriteTime.Month)
$null = New-Item -Path $destination -ItemType Directory -Force
$_ | Move-Item -Destination $destination
}
You can shorten the code by creating tyhe destination path like this: $destination = Join-Path -Path $path -ChildPath ('{0:yyyy\\MM}' -f $_.LastWriteTime), but then you have to remember to escape the backslash in the -f Format template string by doubling it

Powershell move files and folders based on older than x days

I am new to powershell and trying to learn a basic file move from one directory to another. My goal is to move files and folders that are over 18months old to cold storage folder run as a scheduled Task. I need to be able to easily modify it's directories to fit our needs. It needs to preserve the folder structure and only move files that fit the above parameters. I also need it to log everything it did so if something is off i know where.
If I run this it just copies everything. If I comment out the %{Copy-Item... then it runs and lists only based on my parameters and logs it. Where am I going wrong or am I way off base?
Yes it would be easy to use robocopy to do this but I want to use powershell and learn from it.
#Remove-Variable * -ErrorAction SilentlyContinue; Remove-Module *; $error.Clear();
#Clear-Host
#Days older than
$Days = "-485"
#Path Variables
$Sourcepath = "C:\Temp1"
$DestinationPath = "C:\Temp2"
#Logging
$Logfile = "c:\temp3\file_$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).log"
#transcript logs all outputs to txt file
Start-Transcript -Path $Logfile -Append
Get-ChildItem $Sourcepath -Force -Recurse |
Where-Object {$_.LastwriteTime -le (Get-Date).AddDays($Days)} |
% {Copy-Item -Path $Sourcepath -Destination $DestinationPath -Recurse -Force}
Stop-Transcript
Problem
Copy-Item -Path $Sourcepath -Destination $DestinationPath -Recurse -Force
You always specify the same path for source and destination. With parameter -recurse you will copy the whole directory $SourcePath for each matching file.
Solution
You need to feed the output of the previous pipeline steps to Copy-Item by using the $_ (aka $PSItem) variable, basically using Copy-Item in single-item mode.
Try this (requires .NET >= 5.0 for GetRelativePath method):
Get-ChildItem $Sourcepath -File -Force -Recurse |
Where-Object {$_.LastwriteTime -le (Get-Date).AddDays($Days)} |
ForEach-Object {
$relativeSourceFilePath = [IO.Path]::GetRelativePath( $sourcePath, $_.Fullname )
$destinationFilePath = Join-Path $destinationPath $relativeSourceFilePath
$destinationSubDirPath = Split-Path $destinationFilePath -Parent
# Need to create sub directory when using Copy-Item in single-item mode
$null = New-Item $destinationSubDirPath -ItemType Directory -Force
# Copy one file
Copy-Item -Path $_ -Destination $destinationFilePath -Force
}
Alternative implementation without GetRelativePath (for .NET < 5.0):
Push-Location $Sourcepath # Base path to use for Get-ChildItem and Resolve-Path
try {
Get-ChildItem . -File -Force -Recurse |
Where-Object {$_.LastwriteTime -le (Get-Date).AddDays($Days)} |
ForEach-Object {
$relativeSourceFilePath = Resolve-Path $_.Fullname -Relative
$destinationFilePath = Join-Path $destinationPath $relativeSourceFilePath
$destinationSubDirPath = Split-Path $destinationFilePath -Parent
# Need to create sub directory when using Copy-Item in single-item mode
$null = New-Item $destinationSubDirPath -ItemType Directory -Force
# Copy one file
Copy-Item -Path $_ -Destination $destinationFilePath -Force
}
}
finally {
Pop-Location # restore previous location
}
On a side note, $Days = "-485" should be replaced by $Days = -485.
You currently create a string instead of a number and rely on Powershell's ability to automagically convert string to number when "necessary". This doesn't always work though, so better create a variable with the appropriate datatype in the first place.

How to move folders from one location to another using PowerShell

So i have a directory full of folders that i want to move to another area, also i only want to move the folders that were created 30 days ago or more. I have a script that does what i need for files but it doesnt seem to work for folders. Script is below
Script for moving files
param (
[Parameter(Mandatory=$true)][string]$destinationRoot
)
$path = (Get-Item -Path ".\").FullName
Get-ChildItem -Recurse | ?{ $_.PSIsContainer }
Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} |
Foreach-Object {
$content = $path + "\" + $_.Name
$year = (Get-Item $content).LastWriteTime.year.ToString()
$monthNumber = (Get-Item $content).LastWriteTime.month
$month = (Get-Culture).DateTimeFormat.GetMonthName($monthNumber)
$destination = $destinationRoot + "\" + $year + "\" + $month
New-Item -ItemType Directory -Force -Path $destination
Move-Item -Path $content -Destination $destination -force
}
The Get-ChildItem portion does not seem to pull directories in like it should.
So looking at the script i decided to change some things up
Function Move-FilesByAge(){
param (
[Parameter(Mandatory=$true)][string]$Source,
[Parameter(Mandatory=$true)][string]$Destination,
[Parameter(Mandatory=$true)][timespan]$AgeLimit
)
Get-ChildItem $Source -Directory -Recurse | ?{
$($_.CreationTimeUtc.Add($AgeLimit)) -lt $((Get-Date).ToUniversalTime())
} | %{
$Dpath = $Destination + "\" + $_.CreationTimeUtc.ToString("yyyy") + "\" + $_.CreationTimeUtc.ToString("MMMM")
New-Item -ItemType Directory -Force -Path $Dpath
Move-Item $_ -Destination $Dpath -Force
}
}
Move-FilesByAge -Source C:\Test -Destination C:\Test2 -AgeLimit (New-TimeSpan -days 30)
This can lead to a major issue. If a folder with the same name exists then it will pop a error that folder exists.
Since you are new to powershell lets go over some basics about this script. In Powershell we love Piping | which you did well in the original. We also a big fan of aliases Where-Object ?{}, Foreach-Object %{}.
Get-ChildItem has a built in switch for just returning directories -directory.
You are also using last LastWriteTime when you should be using CreationTime. CreationTimeUtc allows you to standardize your time across timezones by providing a base timezone.
Date.ToString(Date Format Here). IS a great way to shorten how you parse the date as a string. .ToString("yyyy") gets you the year in 4 numbers like 2018. .ToString("MMMM") will get the month by name like March.

How to to use .LastWriteTime and (Get-Date).Month to move data into a new Year/Month Folder Structure

I am fairly new to powershell and have been tasked at cleaning up an archive server. I am trying to create a script to move files into a Year\Month folder structure by looking at it's LastWriteTime in Powershell.
I have the below however I do not know how to get it to look at the month the file was edited?
$Path = "D:\Data"
$NewPath = "D:\ArchiveData"
$Year = (Get-Date).Year
$Month = (Get-Date).Month
New-Item $NewPath\ -name $CurrentYear -ItemType Directory
New-Item $NewPath\$Year -Name $Month -ItemType Directory
Get-ChildItem -path $Path | Where-Object {$_.LastWriteTime -Contains (Get-Date).month} | Move-Item -Destination "$NewPath\$Year\$Month"
Any ideas of how I could do this would be appreciated?
Thanks
-contains is used to see if an array contains an item; it's not suitable here.
-eq is what you need. As per your variable $Month, you will need to get only the part you care about (i.e. the month):
($_.LastWriteTime).Month -eq (Get-Date).month
I think I would approach this from the other end. Directories can be created when needed.
When you are satisfied that the files would be moved correctly, remove the -WhatIf from the Move-Item cmdlet.
$Path = 'C:\src\t'
$NewPath = 'C:\src\tarch'
Get-ChildItem -File -Path $Path |
ForEach-Object {
$Year = $_.LastWriteTime.Year
$Month = $_.LastWriteTime.Month
$ArchDir = "$NewPath\$Year\$Month"
if (-not (Test-Path -Path $ArchDir)) { New-Item -ItemType "directory" -Path $ArchDir | Out-Null }
Move-Item -Path $_.FullName -Destination $ArchDir -WhatIf
}