Issue with archiving files - powershell

I am trying to archiving old files in the server (older then 90 days) and it should be separate ZIP file for every month. I have powershell-v1.0 so I am not able to use System.Management.Automation.PSObject
I have created a script but I have a problem with zip file name. When I run the script all files are moving to one archive with name +++.
$folders ="C:\New folder\test\"
Function Zip
{
Param
(
[string]$zipFile
,
[string[]]$toBeZipped
)
$CurDir = Get-Location
Set-Location "C:\program files\7-zip\"
.\7z.exe A -tzip $zipFile $toBeZipped | Out-Null
Set-Location $CurDir
}
$files = Get-ChildItem -Path $folders | Where-Object {$_.LastwriteTime -lt ((Get-date).adddays(-90))} | % { $_.FullName};
if ( !$files)
{break}
else
{
Write-Host $files
$file_year = $files.LastwriteTime.Year
$file_month = $files.LastwriteTime.Month
echo $file_month
ZIP $folders+"$file_year"+"$file_month"+".zip" $files
If(Test-Path $folders+$file_year+$file_month+.zip)
{
Remove-Item $files
}}
It would be nice if someone can figure out what am I doing wrong.

There are two issues why this doesn't work. First, you are selecting (using the ForEach-Object cmdlet %) the FullName property thus won't be able to access the LastWriteTime property anymore. Secondly, you are trying to access the property on a potential array of files (which year and month you want to use?)
So I would change / refactor your script to something like this (untested).
$folders ="C:\New folder\test\"
function Write-ZipFile
{
Param
(
[string]$Path,
[string[]]$Files
)
$7zip = Join-Path $env:ProgramFiles '\7-zip\7z.exe'
& $7zip A -tzip $zipFile $toBeZipped | Out-Null
}
$files = Get-ChildItem -Path $folders |
Where-Object { $_.LastWriteTime -lt ((Get-date).AddDays(-90)) }
$zipFile = Join-Path $folders ('{0}{1}.zip' -f $files[0].LastwriteTime.Year, $files[0].LastwriteTime.Month)
Write-ZipFile -Path $zipFile -Files ($files | select -ExpandProperty FullName)

The list of file names to archive is in $files. Use $files.GetType() to see that it is an array. Use $files[0].GetType() to see that each element is a string type, not a file object type.
$files = Get-ChildItem -Path $folders |
Where-Object {$_.LastwriteTime -lt (Get-date).adddays(-90)}
I imagine that you will want to omit directories.
The $files array will be an array of FileInfo objects, not strings.
Secondly, do something to iterate over the list of FileInfo objects.
[cmdletbinding()]
Param()
$folders = "H:\src\powershell\afm"
Function Zip
{
Param (
[string]$zipFile
, [string[]]$toBeZipped
)
& "C:\Program Files\7-Zip\7z.exe" A -tzip $zipFile $toBeZipped | Out-Null
}
$files = Get-ChildItem -Path $folders -File |
Where-Object {($_.LastwriteTime -lt (Get-date).adddays(-10)) -and ($_.Extension -ne ".zip")}
$files | ForEach-Object {
Write-Verbose "Working on file $_"
$file_year = $_.LastwriteTime.Year
$file_month = $_.LastwriteTime.Month
Write-Verbose "file_month is $file_month"
Zip "$folders\$file_year$file_month.zip" "$folders\$_"
If (Test-Path "$folders\$file_year$file_month.zip")
{
### Remove-Item $_q
}
}
It would appear that the problem is that there is nothing to process each file to archive them. There is no ForEach-Object.
There is no LastWriteTime on an array object, only on a FileInfo object.

Related

copy newer files without keeping folders

I have a folder with a number of subfolders containing files and want to copy all files to the root folder but only overwrite if newer.
In powershell I can do -
Get-ChildItem D:\VaM\Custom\Atom\Person\Morphs\temp2\female -Recurse -file | Copy-Item -Destination D:\VaM\Custom\Atom\Person\Morphs\female
But this will overwrite all files, I only want to overwrite files if the copied file is newer.
robocopy can overwrite only older this but keeps the folder structure.
Try this
$root = 'D:\VaM\Custom\Atom\Person\Morphs\temp2\female'
[bool]$Delete = $false
Get-ChildItem $root -Recurse -File |
Where-Object {$_.DirectoryName -ne $root } | # Do not touch files already seated in root
ForEach-Object {
$rootNameBrother = Get-Item "$root\$($_.Name)" -ea 0
if($rootNameBrother -and $rootNameBrother.LastWriteTime -lt $_.LastWriteTime) {
# RootFile with same name exists and is Older - Copy and override
Copy-Item -Path $_.FullName -Destination $rootNameBrother.FullName -Force
}
elseif ($rootNameBrother -and $rootNameBrother.LastWriteTime -ge $_.LastWriteTime) {
# RootFile with same name exists and is Newer or same Age
# Delete non root File if allowed
if($Delete) { Remove-Item $_.FullName -Force }
}
}
Set...
$Delete = $true
...if you wish to delete non root files that could not be copied because there already was a file with the same name and greater modiefydate in root.
You also can set the
$VerbosePreference = "Continue"
$WhatIfPreference = "Continue"
variables, just to be safe when you execute the script for the first time.
If you wish to delete all empty subfolder, you can run this:
$allFolders =`
Get-ChildItem $root -Recurse -Directory |
ForEach-Object {
# Add now Depth Script Property
$_ | Add-Member -PassThru -Force -MemberType ScriptProperty -Name Depth -Value {
# Get Depth of folder by looping through each letter and counting the backshlashes
(0..($this.FullName.Length - 1) | ForEach {$this.FullName.Substring($_,1)} | Where-Object {$_ -eq "\"}).Count
}
}
# Sort all Folder by new Depth Property annd Loop throught
$allFolders | Sort -Property Depth -Descending |
ForEach-Object {
# if .GetFileSystemInfos() method return null, the folder is empty
if($_.GetFileSystemInfos().Count -eq 0) {
Remove-Item $_.FullName -Force # Remove Folder
}
}
You can do it like this:
$source = 'D:\VaM\Custom\Atom\Person\Morphs\temp2\female'
$destination = 'D:\VaM\Custom\Atom\Person\Morphs\female'
Get-ChildItem -Path $source -Recurse -File | ForEach-Object {
# try and get the existing file in the destination folder
$destFile = Get-Item -Path (Join-Path -Path $destination -ChildPath $_.Name) -ErrorAction SilentlyContinue
if (!$destFile -or $_.LastWriteTime -gt $destFile.LastWriteTime) {
# copy the file if it either did not exist in the destination or if this file is newer
Write-Host "Copying file $($_.Name)"
$_ | Copy-Item -Destination $destination -Force
}
}
I ended up doing this:
Get-ChildItem G:\VaM\Custom\Atom\Person\Morphs\temp2\ -Recurse |
Where-Object { $_.PSIsContainer -eq $true } |
Foreach-Object { robocopy $_.FullName G:\VaM\Custom\Atom\Person\Morphs\female /xo /ndl /np /mt /nfl}
it runs through the directory structure and copys the contents of each directory to the destination but only overwrites older files.

How to Get-childitem for multiple files only dated today

I'm trying to modify a PS script so that it can:
A: Check that multiple files exist and are dated today
B: Do this from multiple locations
$folder = '\\path\subfolder\'
$files = #(
"file1.txt",
"file2.txt",
"file3.txt",
)
Write-Host "Folder: $folder."
# Get only files and only their names
$folderFiles = Get-ChildItem -Path $folder -Recurse -File -Name
foreach ($f in $files) {
if ($folderFiles -contains $f) {
Write-Host "File $f was found." -foregroundcolor green
} else {
Write-Host "File $f was not found!" -foregroundcolor red
}
}
A the moment this script is designed to only look in one folder and not check for files only dated today. I have no clue how to change it to use multiple folder locations.
I guess what you are looking for is something like this:
# create an aray of paths to search through
$folders = '\\server1\share\path\subfolder\', '\\server2\share\path\subfolder\'
# create an array of file names to look for
$files = 'file1.txt', 'file2.txt', 'file3.txt'
# get the current date as of midnight
$refDate = (Get-Date).Date
# retrieve objects from recursing through the array of folders
Get-ChildItem -Path $folders -Include $files -File -Recurse |
Where-Object { $_.LastWriteTime -ge $refDate } |
# output whatever properties you want from the files
Select-Object DirectoryName, Name, LastWriteTime
Parameter -Include is only valid when the path ends with \* OR if the -Recurse switch is used
To get items from today, you could do something similar:
Get-Childitem $folder * -Recurse | Where-Object {$_.CreationTime -gt (Get-Date).Date }
As for the multiple locations - I would suggest creating an array of folders you want to search and then iterating through the array.

Duplicate file name as folder, insert file

I am trying to use Powershell to
scan folder D://Mediafolder for names of media files
create a folder for each media file scanned, with same name
insert each media file in to matching folder name.
I can find no documentation or thread of this, and I am more fluent in Linux than Windows. I've tried many times to piece this together, but to no avail.
Hope this will help :)
This will create a folder for each file with the same name, so if you have a file called xyz.txt, it will create a folder called xyz and move the file to this folder.
$path = "D:\MediaFolder"
$items = Get-ChildItem $path
Foreach ($item in $items)
{
$folderName = $item.name.Split('.')[0]
New-Item "$path\$folderName" -ItemType Directory
Move-Item -Path "$path\$item" -Destination "$path\$foldername"
}
File Sorting based on extension should do the job:
$folder_path = read-host "Enter the folder path without space"
$file = gci $folder_path -Recurse | ? {-not $_.psiscontainer}
$file | group -property extension | % {if(!(test-path(join-path $folder_path -child $_.name.replace('.','')))){new-item -type directory $(join-path $folder_path -child $_.name.replace('.','')).toupper()}}
$file | % { move-item $_.fullname -destination $(join-path $folder_path -child $_.extension.replace(".",""))}
$a = Get-ChildItem $folder_path -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Remove-Item -Force
This will iterate over the files in the media_dir and move those with the extensions in media_types to a folder with the same basename. When you are satisfied that the files will be moved to the correct directory, remove the -WhatIf from the Move-Item statement.
PS C:\src\t> type .\ms.ps1
$media_dir = 'C:\src\t\media'
$new_dir = 'C:\src\t\newmedia'
$media_types = #('.mp3', '.mp4', '.jpeg')
Get-ChildItem -Path $media_dir |
ForEach-Object {
$base_name = $_.BaseName
if ($media_types -contains $_.Extension) {
if (-not (Test-Path $new_dir\$base_name)) {
New-Item -Path $new_dir\$base_name -ItemType Directory | Out-Null
}
Move-Item $_.FullName $new_dir\$base_name -WhatIf
}
}

find all specific folders and zip them

I want to find all folders with name 'test' and to zip them into single folder with different names, of course.
I managed to do some code:
$RootFolder = "E:\"
$var = Get-ChildItem -Path $RootFolder -Recurse |
where {$_.PSIsContainer -and $_.Name -match 'test'}
#this is assembly for zip functionality
Add-Type -Assembly "System.IO.Compression.Filesystem"
foreach ($dir in $var) {
$destination = "E:\zip\test" + $dir.Name + ".zip"
if (Test-Path $destination) {Remove-Item $destination}
[IO.Compression.Zipfile]::CreateFromDirectory($dir.PSPath, $destination)
}
It gives me an error:
Exception calling "CreateFromDirectory" with "2" argument(s): "The given path's format is not supported."
I want to know, what is the right way to pass path of my $dir.
The PSPath property returned from Get-ChildItem is prefixed with the PSProvider. The CreateFromDirectory() method takes two strings; the first is sourceDirectoryName for which you could use Fullname from your object.
$RootFolder = "E:\"
$Directories = Get-ChildItem -Path $RootFolder -Recurse | Where-Object {
$_.PSIsContainer -And
$_.BaseName -Match 'test'
}
Add-Type -AssemblyName "System.IO.Compression.FileSystem"
foreach ($Directory in $Directories) {
$Destination = "E:\zip\test$($Directory.name).zip"
If (Test-path $Destination) {
Remove-Item $Destination
}
[IO.Compression.ZipFile]::CreateFromDirectory($Directory.Fullname, $Destination)
}
If you're using v5, I would suggest using the Commandlet
If you don't want to use the commandlet you can use this:
$FullName = "Path\FileName"
$Name = CompressedFileName
$ZipFile = "Path\ZipFileName"
$Zip = [System.IO.Compression.ZipFile]::Open($ZipFile,'Update')
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($Zip,$FullName,$Name,"optimal")
$Zip.Dispose()
If you have a folder structure like this:
- Folder1
-- Test
- Folder2
-- Test
- Folder3
-- Test
You can do this:
gci -Directory -Recurse -Filter 'test*' | % {
Compress-Archive "$($_.FullName)\**" "$($_.FullName -replace '\\|:', '.' ).zip"
}
And you will get:
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder1.Test.zip
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder2.Test.zip
D..Dropbox.Projects.StackOverflow-Posh.ZipFolders.Folder3.Test.zip
Or if you want to preserve the directory structure inside your zips:
gci -Directory -Recurse -Filter 'test*' | % {
Compress-Archive $_.FullName "$($_.FullName -replace '\\|:', '.' ).zip"
}

How to recursively remove all empty folders in PowerShell?

I need to recursively remove all empty folders for a specific folder in PowerShell (checking folder and sub-folder at any level).
At the moment I am using this script with no success.
Could you please tell me how to fix it?
$tdc='C:\a\c\d\'
$a = Get-ChildItem $tdc -recurse | Where-Object {$_.PSIsContainer -eq $True}
$a | Where-Object {$_.GetFiles().Count -eq 0} | Select-Object FullName
I am using PowerShell on Windows 8.1 version.
You need to keep a few key things in mind when looking at a problem like this:
Get-ChildItem -Recurse performs head recursion, meaning it returns folders as soon as it finds them when walking through a tree. Since you want to remove empty folders, and also remove their parent if they are empty after you remove the empty folders, you need to use tail recursion instead, which processes the folders from the deepest child up to the root. By using tail recursion, there will be no need for repeated calls to the code that removes the empty folders -- one call will do it all for you.
Get-ChildItem does not return hidden files or folders by default. As a result you need to take extra steps to ensure that you don't remove folders that appear empty but that contain hidden files or folders. Get-Item and Get-ChildItem both have a -Force parameter which can be used to retrieve hidden files or folders as well as visible files or folders.
With those points in mind, here is a solution that uses tail recursion and that properly tracks hidden files or folders, making sure to remove hidden folders if they are empty and also making sure to keep folders that may contain one or more hidden files.
First this is the script block (anonymous function) that does the job:
# A script block (anonymous function) that will remove empty folders
# under a root folder, using tail-recursion to ensure that it only
# walks the folder tree once. -Force is used to be able to process
# hidden files/folders as well.
$tailRecursion = {
param(
$Path
)
foreach ($childDirectory in Get-ChildItem -Force -LiteralPath $Path -Directory) {
& $tailRecursion -Path $childDirectory.FullName
}
$currentChildren = Get-ChildItem -Force -LiteralPath $Path
$isEmpty = $currentChildren -eq $null
if ($isEmpty) {
Write-Verbose "Removing empty folder at path '${Path}'." -Verbose
Remove-Item -Force -LiteralPath $Path
}
}
If you want to test it here's code that will create interesting test data (make sure you don't already have a folder c:\a because it will be deleted):
# This creates some test data under C:\a (make sure this is not
# a directory you care about, because this will remove it if it
# exists). This test data contains a directory that is hidden
# that should be removed as well as a file that is hidden in a
# directory that should not be removed.
Remove-Item -Force -Path C:\a -Recurse
New-Item -Force -Path C:\a\b\c\d -ItemType Directory > $null
$hiddenFolder = Get-Item -Force -LiteralPath C:\a\b\c
$hiddenFolder.Attributes = $hiddenFolder.Attributes -bor [System.IO.FileAttributes]::Hidden
New-Item -Force -Path C:\a\b\e -ItemType Directory > $null
New-Item -Force -Path C:\a\f -ItemType Directory > $null
New-Item -Force -Path C:\a\f\g -ItemType Directory > $null
New-Item -Force -Path C:\a\f\h -ItemType Directory > $null
Out-File -Force -FilePath C:\a\f\test.txt -InputObject 'Dummy file'
Out-File -Force -FilePath C:\a\f\h\hidden.txt -InputObject 'Hidden file'
$hiddenFile = Get-Item -Force -LiteralPath C:\a\f\h\hidden.txt
$hiddenFile.Attributes = $hiddenFile.Attributes -bor [System.IO.FileAttributes]::Hidden
Here's how you use it. Note that this will remove the top folder (the C:\a folder in this example, which gets created if you generated the test data using the script above) if that folder winds up being empty after deleting all empty folders under it.
& $tailRecursion -Path 'C:\a'
You can use this:
$tdc="C:\a\c\d"
$dirs = gci $tdc -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName
$dirs | Foreach-Object { Remove-Item $_ }
$dirs will be an array of empty directories returned from the Get-ChildItem command after filtering. You can then loop over it to remove the items.
Update
If you want to remove directories that contain empty directories, you just need to keep running the script until they're all gone. You can loop until $dirs is empty:
$tdc="C:\a\c\d"
do {
$dirs = gci $tdc -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName
$dirs | Foreach-Object { Remove-Item $_ }
} while ($dirs.count -gt 0)
If you want to ensure that hidden files and folders will also be removed, include the -Force flag:
do {
$dirs = gci $tdc -directory -recurse | Where { (gci $_.fullName -Force).count -eq 0 } | select -expandproperty FullName
$dirs | Foreach-Object { Remove-Item $_ }
} while ($dirs.count -gt 0)
Get-ChildItem $tdc -Recurse -Force -Directory |
Sort-Object -Property FullName -Descending |
Where-Object { $($_ | Get-ChildItem -Force | Select-Object -First 1).Count -eq 0 } |
Remove-Item -Verbose
The only novel contribution here is using Sort-Object to reverse sort by the directory's FullName. This will ensure that we always process children before we process parents (i.e., "tail recursion" as described by Kirk Munro's answer). That makes it recursively remove empty folders.
Off hand, I'm not sure if the Select-Object -First 1 will meaningfully improve performance or not, but it may.
Just figured I would contribute to the already long list of answers here.
Many of the answers have quirks to them, like needing to run more than once. Others are overly complex for the average user (like using tail recursion to prevent duplicate scans, etc).
Here is a very simple one-liner that I've been using for years, and works great...
It does not account for hidden files/folders, but you can fix that by adding -Force to the Get-ChildItem command
This is the long, fully qualified cmdlet name version:
Get-ChildItem -Recurse -Directory | ? { -Not ($_.EnumerateFiles('*',1) | Select-Object -First 1) } | Remove-Item -Recurse
So basically...here's how it goes:
Get-ChildItem -Recurse -Directory - Start scanning recursively looking for directories
$_.EnumerateFiles('*',1) - For each directory...Enumerate the files
EnumerateFiles will output its findings as it goes, GetFiles will output when it is done....at least, that's how it is supposed to work in .NET...for some reason in PowerShell GetFiles starts spitting out immediately. But I still use EnumerateFiles because in testing it was reliably faster.
('*',1) means find ALL files recursively.
| Select-Object -First 1 - Stop at the first file found
This was difficult to test how much it helped. In some cases it helped tremendously, other times it didn't help at all, and in some cases it slowed it down by a small amount. So I really don't know. I guess this is optional.
| Remove-Item -Recurse - Remove the directory, recursively (ensures directories that contain empty sub directories gets removed)
If you're counting characters, this could be shortened to:
ls -s -ad | ? { -Not ($_.EnumerateFiles('*',1) | select -First 1) } | rm -Recurse
-s - alias for -Recurse
-ad - alias for -Directory
If you really don't care about performance because you don't have that many files....even more so to:
ls -s -ad | ? {!($_.GetFiles('*',1))} | rm -Recurse
Side note:
While playing around with this, I started testing various versions with Measure-Command against a server with millions of files and thousands of directories.
This is faster than the command I've been using (above):
(gi .).EnumerateDirectories('*',1) | ? {-Not $_.EnumerateFiles('*',1) } | rm -Recurse
ls c:\temp -rec |%{ if ($_.PSIsContainer -eq $True) {if ( (ls $_.fullname -rec | measure |select -expand count ) -eq "0" ){ ri $_.fullname -whatif} } }
Assuming you're inside the parent folder of interest
gci . -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }
For your case with $tdc it'll be
gci $tdc -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }
If you just want to make sure, that you delete only folders that may contain subfolders but no files within itself and its subfolders, this may be an easier an quicker way.
$Empty = Get-ChildItem $Folder -Directory -Recurse |
Where-Object {(Get-ChildItem $_.FullName -File -Recurse -Force).Count -eq 0}
Foreach ($Dir in $Empty)
{
if (test-path $Dir.FullName)
{Remove-Item -LiteralPath $Dir.FullName -recurse -force}
}
Recursively removing empty subdirectories can also be accomplished using a "For Loop".
Before we start, let's make some subdirectories & text files to work with in $HOME\Desktop\Test
MD $HOME\Desktop\Test\0\1\2\3\4\5
MD $HOME\Desktop\Test\A\B\C\D\E\F
MD $HOME\Desktop\Test\A\B\C\DD\EE\FF
MD $HOME\Desktop\Test\Q\W\E\R\T\Y
MD $HOME\Desktop\Test\Q\W\E\RR
"Hello World" > $HOME\Desktop\Test\0\1\Text1.txt
"Hello World" > $HOME\Desktop\Test\A\B\C\D\E\Text2.txt
"Hello World" > $HOME\Desktop\Test\A\B\C\DD\Text3.txt
"Hello World" > $HOME\Desktop\Test\Q\W\E\RR\Text4.txt
First, store the following Script Block in the variable $SB. The variable can be called later using the &SB command. The &SB command will output a list of empty subdirectories contained in $HOME\Desktop\Test
$SB = {
Get-ChildItem $HOME\Desktop\Test -Directory -Recurse |
Where-Object {(Get-ChildItem $_.FullName -Force).Count -eq 0}
}
NOTE: The -Force parameter is very important. It makes sure that directories which contain hidden files and subdirectories, but are otherwise empty, are not deleted in the "For Loop".
Now use a "For Loop" to recursively remove empty subdirectories in $HOME\Desktop\Test
For ($Empty = &$SB ; $Empty -ne $null ; $Empty = &$SB) {Remove-Item (&$SB).FullName}
Tested as working on PowerShell 4.0
I have adapted the script of RichardHowells.
It doesn't delete the folder if there is a thumbs.db.
##############
# Parameters #
##############
param(
$Chemin = "" , # Path to clean
$log = "" # Logs path
)
###########
# Process #
###########
if (($Chemin -eq "") -or ($log-eq "") ){
Write-Error 'Parametres non reseignes - utiliser la syntaxe : -Chemin "Argument" -log "argument 2" ' -Verbose
Exit
}
#loging
$date = get-date -format g
Write-Output "begining of cleaning folder : $chemin at $date" >> $log
Write-Output "------------------------------------------------------------------------------------------------------------" >> $log
<########################################################################
define a script block that will remove empty folders under a root folder,
using tail-recursion to ensure that it only walks the folder tree once.
-Force is used to be able to process hidden files/folders as well.
########################################################################>
$tailRecursion = {
param(
$Path
)
foreach ($childDirectory in Get-ChildItem -Force -LiteralPath $Path -Directory) {
& $tailRecursion -Path $childDirectory.FullName
}
$currentChildren = Get-ChildItem -Force -LiteralPath $Path
Write-Output $childDirectory.FullName
<# Suppression des fichiers Thumbs.db #>
Foreach ( $file in $currentchildren )
{
if ($file.name -notmatch "Thumbs.db"){break}
if ($file.name -match "Thumbs.db"){
Remove-item -force -LiteralPath $file.FullName}
}
$currentChildren = Get-ChildItem -Force -LiteralPath $Path
$isEmpty = $currentChildren -eq $null
if ($isEmpty) {
$date = get-date -format g
Write-Output "Removing empty folder at path '${Path}'. $date" >> $log
Remove-Item -Force -LiteralPath $Path
}
}
# Invocation of the script block
& $tailRecursion -Path $Chemin
#loging
$date = get-date -format g
Write-Output "End of cleaning folder : $chemin at $date" >> $log
Write-Output "------------------------------------------------------------------------------------------------------------" >> $log
Something like this works for me. The script delete empty folders and folders containing only folder (no files, no hidden files).
$items = gci -LiteralPath E:\ -Directory -Recurse
$dirs = [System.Collections.Generic.HashSet[string]]::new([string[]]($items |% FullName))
for (;;) {
$remove = $dirs |? { (gci -LiteralPath $_ -Force).Count -eq 0 }
if ($remove) {
$remove | rm
$dirs.ExceptWith( [string[]]$remove )
}
else {
break
}
}
I wouldn't take the comments/1st post to heart unless you also want to delete files that are nested more than one folder deep. You are going to end up deleting directories that may contain directories that may contain files. This is better:
$FP= "C:\Temp\"
$dirs= Get-Childitem -LiteralPath $FP -directory -recurse
$Empty= $dirs | Where-Object {$_.GetFiles().Count -eq 0 **-and** $_.GetDirectories().Count -eq 0} |
Select-Object FullName
The above checks to make sure the directory is in fact empty whereas the OP only checks to make sure there are no files. That in turn would result in files nexted a few folders deep also being deleted.
You may need to run the above a few times as it won't delete Dirs that have nested Dirs. So it only deletes the deepest level. So loop it until they're all gone.
Something else I do not do is use the -force parameter. That is by design. If in fact remove-item hits a dir that is not empty you want to be prompted as an additional safety.
$files = Get-ChildItem -Path c:\temp -Recurse -Force | where psiscontainer ; [array]::reverse($files)
[Array]::reverse($files) will reverse your items, so you get the lowest files in hierarchy first.
I use this to manipulate filenames that have too long filepaths, before I delete them.
This is a simple approach
dir -Directory | ? { (dir $_).Count -eq 0 } | Remove-Item
This will remove up all empty folders in the specified directory $tdc.
It is also a lot faster since there's no need for multiple runs.
$tdc = "x:\myfolder" # Specify the root folder
gci $tdc -Directory -Recurse `
| Sort-Object { $_.FullName.Length } -Descending `
| ? { $_.GetFiles().Count -eq 0 } `
| % {
if ($_.GetDirectories().Count -eq 0) {
Write-Host " Removing $($_.FullName)"
$_.Delete()
}
}
#By Mike Mike Costa Rica
$CarpetasVacias = Get-ChildItem -Path $CarpetaVer -Recurse -Force -Directory | Where {(gci $_.fullName).count -eq 0} | select Fullname,Name,LastWriteTime
$TotalCarpetas = $CarpetasVacias.Count
$CountSu = 1
ForEach ($UnaCarpeta in $CarpetasVacias){
$RutaCarp = $UnaCarpeta.Fullname
Remove-Item -Path $RutaCarp -Force -Confirm:$False -ErrorAction Ignore
$testCar = Test-Path $RutaCarp
if($testCar -eq $true){
$Datem = (Get-Date).tostring("MM-dd-yyyy HH:mm:ss")
Write-Host "$Datem ---> $CountSu de $TotalCarpetas Carpetas Error Borrando Directory: $RutaCarp" -foregroundcolor "red"
}else{
$Datem = (Get-Date).tostring("MM-dd-yyyy HH:mm:ss")
Write-Host "$Datem ---> $CountSu de $TotalCarpetas Carpetas Correcto Borrando Directory: $RutaCarp" -foregroundcolor "gree"
}
$CountSu += 1
}