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
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.
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)
Tenez compte de quelques points essentiels lorsque vous examinez un problème comme celui-ci:
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'
ls c:\temp -rec |%{ if ($_.PSIsContainer -eq $True) {if ( (ls $_.fullname -rec | measure |select -expand count ) -eq "0" ){ ri $_.fullname -whatif} } }
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}
}
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.
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
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.
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