web-dev-qa-db-fra.com

Comment supprimer de manière récursive tous les dossiers vides dans PowerShell?

Je dois supprimer récursivement tous les dossiers vides d'un dossier spécifique dans PowerShell (dossier checing et sous-dossier à n'importe quel niveau).

Pour le moment, j'utilise ce script sans succès.

Pourriez-vous s'il vous plaît me dire comment résoudre ce problème?

$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

J'utilise PowerShell sur Win 8.1

21
GibboK

Vous pouvez utiliser ceci:

$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 sera un tableau de répertoires vides renvoyés à partir de la commande Get-ChildItem après filtrage. Vous pouvez ensuite faire une boucle dessus pour supprimer les éléments.

Mettre à jour

Si vous souhaitez supprimer des répertoires contenant des répertoires vides, il vous suffit de continuer à exécuter le script jusqu'à leur disparition. Vous pouvez boucler jusqu'à ce que $dirs soit vide:

$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)

Si vous voulez vous assurer que les fichiers et dossiers cachés seront également supprimés, incluez l'indicateur -Force:

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

Tenez compte de quelques points essentiels lorsque vous examinez un problème comme celui-ci:

  1. Get-ChildItem -Recurse effectue une récursion de la tête, ce qui signifie qu'il renvoie les dossiers dès qu'il les trouve lors de la navigation dans une arborescence. Étant donné que vous souhaitez supprimer les dossiers vides, ainsi que leurs parents s'ils sont vides une fois que vous avez supprimé les dossiers vides, vous devez utiliser la récursion détaillée, qui traite les dossiers du plus profond des enfants jusqu'à la racine. En utilisant la récursion par la queue, il ne sera pas nécessaire d'appeler de nouveau le code qui supprime les dossiers vides. Un seul appel le fera pour vous.
  2. Get-ChildItem ne renvoie pas de fichiers ou de dossiers cachés par défaut. Par conséquent, vous devez prendre des mesures supplémentaires pour vous assurer de ne pas supprimer les dossiers qui semblent vides, mais qui contiennent des fichiers ou des dossiers cachés. Get-Item et Get-ChildItem ont tous deux un paramètre -Force qui peut être utilisé pour récupérer des fichiers ou des dossiers cachés ainsi que des fichiers ou des dossiers visibles.

Tenant compte de ces points, voici une solution qui utilise la récursion détaillée et qui suit correctement les fichiers ou dossiers cachés, en veillant à supprimer les dossiers cachés s’ils sont vides et en veillant à conserver les dossiers pouvant contenir un ou plusieurs fichiers cachés:

# 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

Si vous voulez simplement vous assurer que vous ne supprimez que les dossiers pouvant contenir des sous-dossiers, mais aucun fichier en lui-même et ses sous-dossiers, cela peut être plus simple et plus rapide.

$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

Je ne prendrais pas les commentaires/le premier message à cœur, sauf si vous souhaitez également supprimer les fichiers imbriqués dans plusieurs dossiers. Vous allez finir par supprimer des répertoires pouvant contenir des répertoires pouvant contenir des fichiers. C'est mieux:

$ FP = "C:\Temp \"

$ dirs = Get-Childitem -LiteralPath $ FP -directory -recurse

$ Vide = $ dirs | Where-Object {$ .GetFiles (). Compter -eq 0 et $ .GetDirectories (). Compter -eq 0} | 

Nom complet d'objet sélectionné

Les vérifications ci-dessus vérifient que le répertoire est en fait vide, alors que l'OP vérifie uniquement qu'il n'y a pas de fichiers. Cela se traduirait par la suppression des fichiers suivants de quelques dossiers en profondeur.

Vous devrez peut-être exécuter le programme ci-dessus plusieurs fois, car cela ne supprimera pas les Dirs contenant des Dirs imbriqués. Donc, il ne supprime que le niveau le plus profond. Alors faites-le en boucle jusqu'à ce qu'ils soient tous partis. 

Une autre chose que je ne fais pas est d'utiliser le paramètre -force. C'est par conception. Si en fait remove-item frappe un répertoire qui n'est pas vide, vous souhaitez que la sécurité soit invitée. 

0
Zack A

La suppression récursive de sous-répertoires vides peut également être effectuée à l'aide d'une "boucle en boucle".

Avant de commencer, créons quelques sous-répertoires et fichiers texte dans $ 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

Commencez par stocker le bloc de script suivant dans la variable $ SB. La variable peut être appelée ultérieurement à l'aide de la commande & SB. La commande & SB affichera une liste de sous-répertoires vides contenus dans $ HOME\Desktop\Test

$SB = {
    Get-ChildItem $HOME\Desktop\Test -Directory -Recurse |
    Where-Object {(Get-ChildItem $_.FullName -Force).Count -eq 0}
}

REMARQUE: le paramètre -Force est très important. Il s'assure que les répertoires contenant des fichiers cachés et des sous-répertoires, mais vides du tout, ne sont pas supprimés dans la boucle "For".

Maintenant, utilisez une boucle "For" pour supprimer récursivement les sous-répertoires vides dans $ HOME\Desktop\Test

For ($Empty = &$SB ; $Empty -ne $null ; $Empty = &$SB) {Remove-Item (&$SB).FullName}

Testé comme fonctionnant sur 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

La seule nouvelle contribution consiste ici à utiliser Sort-Object pour effectuer un tri inverse dans le répertoire FullName. Cela garantira que nous traitons toujours les enfants avant de traiter les parents. Cela rend récursivement supprimer les dossiers vides.

Je ne suis pas sûr si le Select-Object -First 1 va réellement améliorer les performances ou pas, mais c'est possible.

0
Bacon Bits

J'ai adapté le script de RichardHowells… .. Il ne supprime pas le dossier s'il existe un 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
0
user1722888