Post-order Traverse in PowerShell recurse - powershell

Get-ChildItem -Recurse in powershell currently traverse a directory in level order fashion. Is there any way to traverse a directory in post-order way in Powershell?
I am trying to delete files which are older than certain times. and after deleting files, if subfolder is empty, delete that folder too. Right now am doing this.
$path = 'D:\Files'
Get-ChildItem -Path $path -Recurse | Where-Object {
(($_.LastWriteTime -lt (Get-Date).AddDays(-30)) -and ($_ -is [system.io.fileinfo]) )
} | Remove-Item
Get-ChildItem -Path $path -Recurse | Where-Object {
($_ -is [System.IO.DirectoryInfo]) -and $_.CreationTime -lt (Get-Date).AddDays(-30) -and ((Get-ChildItem $_.FullName).Count -eq 0)
} | Remove-Item -Force
But I want to do it in a single command. Not as two different command.

You could reverse the order of the items returned by Get-ChildItem with [Array]::Reverse
Full script:
$items = Get-ChildItem 'D:\Files' -Recurse
[Array]::Reverse($items)
$date = (Get-Date).AddDays(-30)
foreach ($item in $items) {
if ($item.PSIsContainer) {
if ($item.CreationTime -lt $date -and (Get-ChildItem $item.FullName).Count -eq 0) {
Remove-Item $item.FullName
}
}
elseif ($item.LastWriteTime -lt $date) {
Remove-Item $item.FullName
}
}

I couldn't get the post order working properly with GCI, someone claimed it should, but it wasn't traversing depth first. Below is a naive implementation of the classic post order algorithm using the push directory and pop directory commands. Put your "actions" where Write-Host is.
function PostOrder($d){
pushd $d
$folders = Get-ChildItem .\ -Directory -Force
foreach ($folder in $folders){
PostOrder($folder)
}
popd
Write-Host $d.FullName
}
PostOrder("C:\myFolder")

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 }

Powershell - Select-String Advice needed

My goal is to create a PS script that does the following:
a) scans a source directory and produces a list of files that have a matching pattern that I pass in found inside the files
b) take the list of those files and Copy-Item to move and then archive it.
I have this process working for when I "-Filter" on a filename, but can't seem to get my script to work when using "Select-String -pattern". When it gets to the "$FileNames = #($Files | %{$_.Path.Substring($Source.Length)})" part of the code, it says file does not exist as its passing in the #{Path} code?
View of error
If ({$PatternIdentifier -ne "" -and $FileIdentifier -eq "" -and $FileExtension -ne ""})
{
$Files = get-childitem $Source -Filter $FileExtension | Select-String -pattern $PatternIdentifier -SimpleMatch |Select Path
}
$FileNames = #($Files | %{$_.Path.Substring($Source.Length)})
if($Files.Count -ne 0)
{
if ((test-path $ArchiveDestination) -eq 0)
{
New-Item -ItemType Directory -Force -Path $ArchiveDestination
}
foreach ($File in $Files)
{
Copy-Item $File -Destination $DestinationFolder
Copy-Item $File -Destination $ArchiveDestination
$count++
}
if($error.length -lt 0)
{
Write-Host ("Copied {0} files!!" -f $count)
$answer = $FilesTotalCount -$count
}
}
Get the value from the property:
|Select -expand path

Powershell exclude items when copying not working

I am willing to copy recursively from one dir to another by excluding some items that cause errors if copied.
It was working fine when I only had the "System Volume..." and "RECYCLE.BIN" values in "$Excluir" variable, but, now I need to exclude every item that starts with a dot "." so I wrote the loop you see after it that adds new values to the $Excluir variabl.
Now the "-Exclude" parameter forgets excluding the new values, in this case these values are names of files and directories that start with a dot "."
Sorry if my english causes confusions.
This is my code:
$RutaOrigenFicheros = "E:\"
$RutaTempFicheros = "A:\ENTRADA\TMP\"
$Excluir = #( "System Volume Information","`$RECYCLE.BIN")
$ElementosOcultosUnix = Get-ChildItem $RutaOrigenFicheros -Name -Recurse -Include ".*"
foreach ($i in $ElementosOcultosUnix){
if ($i -match "\\")
{
$elemento = $i -split "\\"
$n = ($elemento.length) - 1
$Excluir += $elemento[$n]
}
else
{
$Excluir += $i
}
}
Write-Host $Excluir
Copy-Item -Path $RutaOrigenFicheros* -Destination $RutaTempFicheros -Recurse -Force -Exclude $Excluir -ErrorAction Stop
Your logic isn't very clear (or doesn't make sense). Here's what I think you're trying to accomplish with your code:
$Destination='A:\Temp\'
$Exclude = Get-ChildItem -Path 'E:\' -Recurse -Force |
Where-Object { $_.FullName -like '*System Volume Information*' -or
$_.FullName -like '*$RECYCLE.BIN*' -or
$_.FullName -like '*\.*' }
Write-Host $Exclude.FullName
Copy-Item -Path 'E:\*' -Destination $Destination -Recurse -Force -Exclude $Exclude.FullName -ErrorAction 'Stop'

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

Deleting files in subfolders older than x days

I am trying to delete all files in subfolders of a given folder without deleting the subfolders themselves. I have tried various examples but they are not doing what I expect.
The basic structure of the file tree I am trying to negotiate is:
C:\Backups\Subfolder1
C:\Backups\Subfolder2
C:\Backups\Subfolder3
and I am using the following code:
$limit = (Get-Date).AddDays(-14)
$path = "C:\Backups"
Get-ChildItem -Path $path -Recurse -Force |
Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } |
Remove-Item -WhatIf
I am finding that the files in the subdirectories are being ignored despite the -Recurse flag being used, although any files in the C:\Backups directory are included. If I remove the !$_.PSIsContainer clause the file in the subfolder is included but so are all of the directories.
Can anyone show me how to include the files in the subfolder while still ignoring the subfolders themselves?
If you are using powershell 3.0 you can simply use the -File switch:
$limit = (Get-Date).AddDays(-14)
$path = "C:\Backups"
Get-ChildItem -Path $path -File -Recurse | where { $_.LastWriteTime -lt $limit } | Remove-Item
The below code works for me. Are you on an older version of powershell?
$limit = (Get-Date).AddDays(-14)
$path = "C:\Users\nathan.cooper\Documents"
Get-ChildItem -Path $path -Recurse -Force |
where { (! $_.PSIsContainer) -and ($_.LastWriteTime -lt $limit) } |
Remove-Item -whatif
$limit = (Get-Date).AddDays(-14)
$files = Get-ChildItem -Path C:\Backups -Directory:$false -Recurse
$files | ForEach-Object -Process {
if($_.CreationTime -lt $limit)
{
Remove-Item $_.FullName -Force
}
}
The -Directory:$False parameter means you don't have to worry about deleting any directories because they won't be included in the array.