Moves files into new subfolder for each year/month - powershell

I am trying to move all files under each subfolder into another folder with subfoldername_mmm_yyyy name.
Below codes only move all files in all sub folders into one folder with the name subfoldername_subfoldername_mmm_yyyy.
I know my for each loops are incorrect but I don't know how to fix them, would someone please help?
$curr_date = Get-Date
$folder_path = "C:\Logs\"
$file_type = "C:\Logs\*.log*"
$destination = "C:\Archive\"
# delete tmp files if existed
$deletefile = Get-ChildItem $destination -recurse -include *.7z.tmp -force | remove-item
# set min age of files
$max_days = "-1"
# determine how far back we go based on current date
$zip_date = $curr_date.AddDays($max_days)
Get-ChildItem -Path $folder_path | Where-Object { $_.Attributes -eq "Directory" } | foreach {
# obtain all subfolders
$servername = Get-ChildItem -Path $folder_path | Where-Object { $_.Attributes -eq "Directory" }
# move all files into servername_mmm_yyyy folder
Get-ChildItem $file_type -Recurse | Where-Object { ($_.LastWriteTime -lt $zip_date) -and ($_.psIsContainer -eq $false)}| foreach {
$x = $_.LastWriteTime.ToShortDateString()
$month_year = Get-Date $x -Format MMM_yyyy
$file_destination = ($destination) + ($servername) + "_" + ($month_year)
if (test-path $file_destination) {
move-item $_.fullname $file_destination
}
else {
new-item -ItemType directory -Path $file_destination
move-item $_.fullname $file_destination
}
}
}

Change:
Get-ChildItem $file_type -Recurse | Where-Object { ($_.LastWriteTime -lt $zip_date) -and ($_.psIsContainer -eq $false)} ...
to
Get-ChildItem $file_type -Recurse | Where-Object { ($_.LastWriteTime -lt $zip_date) -and ($_.psIsContainer -eq $false) -and $_.Name.EndsWith(".log")}
and that should get you just the files you want to target

I figured out my problem. Here is how I do it.
I just need to obtain the folder name of each file and build the destination path. Therefore, I only need one foreach loop.
# move all files into servername_mmm_yyyy folder
Get-ChildItem $file_path -Recurse | Where-Object { ($_.LastWriteTime -lt $zip_date) -and ($_.psIsContainer -eq $false)}| foreach {
$x = $_.LastWriteTime.ToShortDateString()
$month_year = Get-Date $x -Format MMM_yyyy
$servername = $_.Directory.Name
$file_destination = ($destination)+($servername)+"_"+($month_year)
if (test-path $file_destination){
copy-item $_.fullname $file_destination
}
else {
new-item -ItemType directory -Path $file_destination
copy-item $_.fullname $file_destination
}
}

Related

Deleting files with Powershell recursively

I got somehow a weird issue, I am trying to use a process to automate the deletion of files from a folder and also child folders, I am trying to delete only files older than 7 days.
My code works but it deletes files that are under 7 days when going recursively into child items. . .anyone could lend a hand here? I just need to delete in each folder/sub-folder the files older than 7 days.
Param (
[string]$Source = "C:\Users\Loredanes\Downloads\",
[string]$Days = "1"
)
$Files = Get-ChildItem $Source -Recurse | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -lt (get-date).addminutes(-$($Days)) }
$Files | Remove-Item -Force
if ($Files.count -gt 0)
{
$Folders = #()
ForEach ($Folder in (Get-ChildItem -Path $Source -Recurse -Directory))
{
$Folders += New-Object PSObject -Property #{
Object = $Folder
Depth = ($Folder.FullName.Split("\")).Count
}
}
$Folders = $Folders | Sort Depth -Descending
ForEach ($Folder in $Folders)
{
If ($Folder.Object.GetFileSystemInfos().Count -eq 0)
{
Write-Host "Removing Folder: $($Folder)"
Remove-Item -Path $Folder.Object.FullName -Force
}
}
}
else
{
Write-Host "No Empty folders found after removing files older than $($Days) days."
}
This should do what you want:
$source = 'D:\Test'
$days = 7
# Remove Files
Get-ChildItem $Source -Recurse | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -lt (get-date).AddDays(-$($days)) } | % { Remove-Item -Path $_.FullName -Force }
# Remove empty directories
Get-ChildItem $source -Recurse | Where-Object { $_.PSIsContainer -and $_.GetFiles("*.*").Count -le 0 } | % { Remove-Item -Path $_.FullName -Force }

Copy everything except files on the list

I am trying to copy all files recursively from a:\ to b:\, except those whose metadata is present in a:\list.txt. The list.txt pattern is LastWriteTimeYYYY-MM-DD HH:MM:SS,size,.fileextension, for example:
2001-01-31 23:59:59,12345,.doc
2001-01-31 23:59:59,12345,.txt
2001-01-31 23:59:00,456,.csv
...so any and all files, anywhere in the a:\ dir tree, matching these metadata should not be copied.
I seem to be having trouble with the Where-Object in order to exclude the items on the list.txt, but copy everything else:
$Source = "C:\a"
$Target = "C:\b"
$List = Import-Csv list.txt -Header LastWriteTime,Size,Name
$Hash = #{}
ForEach ($Row in $List){
$Key = ("{0},{1},.{2}" -F $Row.LastWriteTime,$Row.Size,$Row.Name.Split('.')[-1].ToLower())
IF (!($Hash[$Key])) {$Hash.Add($Key,$Row.Name)}
}
$Hash | Format-Table -Auto
Get-Childitem -Path $Source -Recurse -File | Where-Object {$Hash -eq $Hash[$Key]}| ForEach-Object {$Key = ("{0},{1},{2}" -F ($_.LastWriteTime).ToString('yyyy-MM-dd HH:mm:ss'),$_.Length,$_.Extension.ToLower())
#$Key
If ($Hash[$Key]){
$Destination = $_.FullName -Replace "^$([RegEx]::Escape($Source))","$Target"
If (!(Test-Path (Split-Path $Destination))){MD (Split-Path $Destination)|Out-Null}
$_ | Copy-Item -Destination $Destination
}
}
I propose you a simplification of your code :
$Source = "C:\a\"
$Target = "C:\b\"
New-Item -ItemType Directory $Target -Force | Out-Null
$List = Import-Csv list.txt -Header LastWriteTime,Length,Extension
Get-Childitem $Source -Recurse -File | %{
$File=$_
$exist=$List | where {$_.LastWriteTime -eq $File.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss') -and $_.Length -eq $File.Length -and $_.Extension -eq $File.Extension} | select -first 1
if ($exist -ne $null) {continue}
New-Item -ItemType Directory $File.DirectoryName.Replace($Source, $Target) -Force | Out-Null
Copy-Item $File.FullName $File.FullName.Replace($Source, $Target) -Force
}

Powershell delete Folder if all Files older than x days

I'm new at PowerShell and don't know so much about it.
I'm searching for a way to delete a folder and all sub-folders if all files in this are older than x days. I have an code to delete all files in a folder and all sub-folders but I don't know how to change it right.
$Now = Get-Date
$Days = "30"
$TargetFolder = "C:\temp"
$Extension = "*.*"
$LastWrite = $Now.AddDays(-$Days)
$Files = Get-Childitem $TargetFolder -Include $Extension -Recurse | Where {($_.CreationTime -le "$LastWrite") -and ($_.LastWriteTime -le "$LastWrite")}
foreach ($File in $Files)
{
if ($File -ne $NULL)
{
write-host "Deleting File $File" -ForegroundColor "Red"
Remove-Item $Location.FullName | out-Null
}
else
{
Write-Host "No more files to delete!" -foregroundcolor "Green"
}
}
Enumerate all folders and sort them longest path first, so you process the directories bottom to top:
Get-ChildItem $TargetFolder -Recurse -Directory |
Select-Object -Expand FullName |
Sort-Object Length -Desc
Filter the list for directories that don't have any file or folder newer than x days in them:
... | Where-Object {
-not $(Get-ChildItem $_ -Recurse | Where-Object {
$_.Creationtime -ge $LastWrite -or
$_.LastWriteTime -ge $LastWrite
})
}
Then remove the resulting folders:
... | Remove-Item -Recurse -Force

PowerShell - Get-GhildItem - Ignore specific directory

I am working with a script to clear old files off our file server. We are using this line in the script to find all files older than a certain date:
$oldFiles = Get-ChildItem $oldPath -Recurse | Where-Object { $_.lastwritetime -le $oldDate }
My question is, how do I ignore a certain directory in the $oldPath? For instance, if we had the following:
root
dir1
dir 2
subdir 1
subdir 2
dir 3
subdir 1
dir 4
And we want to ignore dir 2 and all subdirectories when building the list
Final working script:
$oldPath = "\\server\share"
$newDrive = "I:"
$oldDate = Get-Date -Date 1/1/2012
$oldFiles = Get-ChildItem $oldPath -Recurse -File | Where-Object {($_.PSParentPath -notmatch '\\Ignore Directory') -and $_.lastwritetime -le $oldDate }
$oldDirs = Get-ChildItem $oldPath -Recurse | Where-Object {$_.PSIsContainer -and ($_.PSParentPath -notmatch '\\Ignore Directory')} | select-object FullName
$oldDirs = $oldDirs | select -Unique
foreach ($oldDir in $oldDirs) {
$strdir = $newDrive + "\" + ($oldDir | Split-Path -NoQualifier | Out-String).trim().trim("\")
if (!(Test-Path $strdir)) {
Write-Host "$strdir does not exist. Creating directory..."
mkdir $strdir | Out-Null
} # end if
} # end foreach
foreach ($file in $oldFiles) {
$strfile = $newDrive + "\" + ($file.FullName | Split-Path -NoQualifier | Out-String).trim().trim("\")
Write-Host "Moving $file.FullName to $strfile..."
Move-Item $file.FullName -Destination $strfile -Force -WhatIf
} # end foreach
$oldfiles | select pspath | Split-Path -NoQualifier | Out-File "\\nelson\network share\ArchivedFiles.txt"
Modify your Where-Object condition to:
... | Where-Object {($_.PSParentPath -notmatch '\\dir 2') -and ($_.lastWriteTime -le $oldDate)}
Also, you probably want to filter out directory items as well so that $oldFiles contains only files e.g.:
$oldFiles = Get-ChildItem $oldPath -Recurse | Where {!$_.PSIsContainer -and ($_.PSParentPath -notmatch '\\dir 2') -and ($_.lastWriteTime -le $oldDate)}
If you're on PowerShell v3 you can use a new parameter on Get-ChildItem to simplify this to:
$oldFiles = Get-ChildItem $oldPath -Recurse -File | Where {($_.PSParentPath -notmatch '\\dir 2') -and ($_.lastWriteTime -le $oldDate)}
Something like this should work:
$exclude = Join-Path $oldPath 'dir 2'
$oldFiles = Get-ChildItem $oldPath -Recurse | ? {
-not $_.PSIsContainer -and
$_.FullName -notlike "$exclude\*" -and
$_.LastWriteTime -le $oldDate
}
Try $oldFiles = Get-ChildItem $oldPath -Recurse -Exclude "dir 2" | Where-Object { $_.lastwritetime -le $oldDate}

Delete files which are not locked (in use)

I'm trying to delete all files (not folders) in %TEMP% which are older than 30 days. The problem is that some files are in use by a program so they can not be deleted. I tried to solve the problem as follow:
function IsFileLocked($filePath){
#write-host $filePath
Rename-Item $filePath $filePath -ErrorVariable errs -ErrorAction SilentlyContinue
$errs.Count
if ($errs.Count -ne 0)
{
return $true #File is locked
}
else
{
return $false #File is not locked
}
}
$Path= "$env:temp"
if ((Test-Path -Path $Path) -ieq $true)
{
$Daysback = '-30'
$CurrentDate = Get-Date
$DatetoDelete = $CurrentDate.AddDays($Daysback)
get-childitem $Path -recurse | Where-Object {$_.LastWriteTime -lt $DatetoDelete } |
Where-Object {$_.PSIsContainer -eq $False }| Where-Object {(IsFileLocked -filePath "($_)") -eq $false }# | remove-item -force #-WhatIf
}
The problem is that (IsFileLocked -filePath "($_)") -eq $false doesn't return any element.
Is it possible that get-childitem blocks the files, so that all of them are locked when I run get-childitem?
Any other ideas how to solve this problem?
How about just removing files older than 30 days and ignore the errors:
$old = (Get-Date).AddDays(-30)
Get-ChildItem $env:TEMP -Recurse |
Where-Object {!$_.PSIsContainer -and $_.LastWriteTime -lt $old } |
Remove-Item -Force -ErrorAction SilentlyContinue