Ich muss rekursiv alle leeren Ordner für einen bestimmten Ordner in PowerShell entfernen (checing-Ordner und -Unterordner auf einer beliebigen Ebene).
Im Moment verwende ich dieses Skript ohne Erfolg.
Könnten Sie mir bitte sagen, wie ich das beheben kann?
$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
Ich verwende PowerShell unter Win 8.1
Sie können dies verwenden:
$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
ist ein Array leerer Verzeichnisse, die nach dem Filtern vom Befehl Get-ChildItem
zurückgegeben werden. Sie können dann eine Schleife darüber ziehen, um die Elemente zu entfernen.
Wenn Sie Verzeichnisse entfernen möchten, die leere Verzeichnisse enthalten, müssen Sie das Skript nur so lange ausführen, bis alle gelöscht sind. Sie können eine Schleife machen, bis $dirs
leer ist:
$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)
Wenn Sie sicherstellen möchten, dass auch versteckte Dateien und Ordner entfernt werden, geben Sie das Flag -Force
an:
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)
Wenn Sie sich ein Problem wie dieses ansehen, müssen Sie einige wichtige Punkte beachten:
In Anbetracht dieser Punkte ist hier eine Lösung, die die Rekursion Tail verwendet und verborgene Dateien oder Ordner ordnungsgemäß verfolgt. Dabei müssen Sie sicherstellen, dass versteckte Ordner entfernt werden, wenn sie leer sind. Außerdem sollten Sie Ordner mit mindestens einer versteckten Datei behalten:
# First create 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
# Now 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
$isEmpty = $currentChildren -eq $null
if ($isEmpty) {
Write-Verbose "Removing empty folder at path '${Path}'." -Verbose
Remove-Item -Force -LiteralPath $Path
}
}
# Lastly invoke the script block and pass in a root path where
# you want it to start. This will remove all empty folders in
# the folder you specify, including empty folders that contain
# nothing but empty folders, including the start folder if that
# winds up as empty.
& $tailRecursion -Path 'C:\a'
ls c:\temp -rec |%{ if ($_.PSIsContainer -eq $True) {if ( (ls $_.fullname -rec | measure |select -expand count ) -eq "0" ){ ri $_.fullname -whatif} } }
Wenn Sie nur sicherstellen möchten, dass Sie nur Ordner löschen, die Unterordner enthalten können, aber keine Dateien in sich selbst und seinen Unterordnern, kann dies auf schnellere Weise einfacher sein.
$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}
}
Ich würde den Kommentar/1. Beitrag nicht zu Herzen nehmen, wenn Sie auch Dateien löschen möchten, die mehr als einen Ordner tief verschachtelt sind. Sie werden am Ende Verzeichnisse löschen, die Verzeichnisse enthalten können, die Dateien enthalten können. Das ist besser:
$ FP = "C:\Temp"
$ dirs = Get-Childitem -LiteralPath $ FP -Verzeichnis -recurse
$ Empty = $ dirs | Where-Object {$ .GetFiles (). Count -eq 0 -and $ .GetDirectories (). Count -eq 0} |
Select-Object FullName
Das obige überprüft, ob das Verzeichnis tatsächlich leer ist, während das OP nur überprüft, ob keine Dateien vorhanden sind. Dies würde wiederum dazu führen, dass Dateien, die ein paar Ordner tief waren, ebenfalls gelöscht wurden.
Möglicherweise müssen Sie den obigen Befehl einige Male ausführen, da er keine Dirs mit verschachtelten Dirs löscht. So wird nur die tiefste Ebene gelöscht. Also schleife es, bis sie alle weg sind.
Was ich sonst nicht mache, ist den -force-Parameter zu verwenden. Das ist beabsichtigt. Wenn remove-item tatsächlich ein Verzeichnis trifft, das nicht leer ist, möchten Sie als zusätzliche Sicherheitsabfrage aufgefordert werden.
So etwas funktioniert bei mir. Das Skript löscht leere Ordner und Ordner, die nur Ordner enthalten (keine Dateien, keine versteckten Dateien).
$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
}
}
Das rekursive Entfernen leerer Unterverzeichnisse kann auch mit einer "For-Schleife" durchgeführt werden.
Bevor wir beginnen, erstellen wir einige Unterverzeichnisse und Textdateien, die in $ HOME\Desktop\Test verwendet werden können
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
Speichern Sie zuerst den folgenden Skriptblock in der Variablen $ SB. Die Variable kann später mit dem Befehl & SB aufgerufen werden. Der Befehl & SB gibt eine Liste leerer Unterverzeichnisse aus, die in $ HOME\Desktop\Test enthalten sind
$SB = {
Get-ChildItem $HOME\Desktop\Test -Directory -Recurse |
Where-Object {(Get-ChildItem $_.FullName -Force).Count -eq 0}
}
HINWEIS: Der Parameter "Force" ist sehr wichtig. Es wird sichergestellt, dass Verzeichnisse, die versteckte Dateien und Unterverzeichnisse enthalten, ansonsten jedoch leer sind, nicht in der "For-Schleife" gelöscht werden.
Verwenden Sie jetzt eine "For-Schleife", um leere Unterverzeichnisse in $ HOME\Desktop\Test rekursiv zu entfernen
For ($Empty = &$SB ; $Empty -ne $null ; $Empty = &$SB) {Remove-Item (&$SB).FullName}
Getestet als Arbeit an PowerShell 4.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
Der einzige neuartige Beitrag hier ist die Verwendung von Sort-Object
zum Umkehren der Sortierung nach dem Verzeichnis FullName. Dadurch wird sichergestellt, dass wir Kinder immer verarbeiten, bevor wir Eltern bearbeiten. Dadurch werden leere Ordner rekursiv entfernt.
Ich bin mir nicht sicher, ob der Select-Object -First 1
die Leistung sinnvoll verbessert oder nicht, aber es kann sein.
Angenommen, Sie befinden sich im gewünschten übergeordneten Ordner
gci . -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }
Für Ihren Fall mit $tdc
Wird es sein
gci $tdc -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }
Ich habe das Skript von RichardHowells angepasst. Es löscht den Ordner nicht, wenn eine thumbs.db vorhanden ist.
##############
# 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