wake-up-neo.net

Wie werden alle leeren Ordner in PowerShell rekursiv entfernt?

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

21
GibboK

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.

Aktualisieren

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)
23
arco444

Wenn Sie sich ein Problem wie dieses ansehen, müssen Sie einige wichtige Punkte beachten:

  1. Get-ChildItem -Recurse führt eine Rekursion des Heads aus, dh es werden Ordner zurückgegeben, sobald sie beim Durchlaufen eines Baums gefunden werden. Da Sie leere Ordner entfernen möchten und auch deren übergeordnete Elemente entfernen möchten, wenn diese leer sind, nachdem Sie die leeren Ordner entfernt haben, müssen Sie stattdessen die Endrekursion verwenden, die die Ordner vom tiefsten untergeordneten Element bis zum Stammverzeichnis verarbeitet. Durch die Verwendung der Endrekursion sind keine wiederholten Aufrufe des Codes erforderlich, durch den die leeren Ordner entfernt werden. Ein Anruf erledigt alles für Sie.
  2. Get-ChildItem gibt standardmäßig keine versteckten Dateien oder Ordner zurück. Daher müssen Sie zusätzliche Schritte unternehmen, um sicherzustellen, dass Sie keine Ordner entfernen, die leer erscheinen, die jedoch versteckte Dateien oder Ordner enthalten. Get-Item und Get-ChildItem verfügen beide über einen -Force-Parameter, mit dem versteckte Dateien oder Ordner sowie sichtbare Dateien oder Ordner abgerufen werden können.

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'
29
Kirk Munro
ls c:\temp -rec |%{ if ($_.PSIsContainer -eq $True) {if ( (ls $_.fullname -rec | measure |select -expand count ) -eq "0"  ){ ri $_.fullname -whatif}  }  }  
4
Loïc MICHEL

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}
}
0
user9913445

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. 

0
Zack A

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
    }
}
0
unlikely

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

0
Zelda64
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.

0
Bacon Bits

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} }

0
DeepSpace101

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
0
user1722888