windows - PowerShell: Find og slet mapper, der ikke har filer i dem eller i børnemapper

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har et PowerShell 2.0 script, som jeg bruger til at slette mapper, der ikke har nogen filer i dem:


dir 'P:path	owherever' -recurse | Where-Object { $\_.PSIsContainer } | Where-Object { $\_.GetFiles().Count -eq 0 } | foreach-object { remove-item $\_.fullname -recurse}


Jeg bemærkede dog, at der var mange fejl ved kørsel af scriptet. nemlig:


Remove-Item : Directory P:path	owherever cannot be removed because it is not empty.


'HVAD?!' Jeg panikede. De skal alle være tomme! Jeg filtrerer kun for tomme mapper! Det er tilsyneladende ikke rigtigt, hvordan scriptet virker. I dette scenarie anses en mappe, der kun har mapper som børn, men filer som børnebørn anses for tomme af filer:


Folder1 (no files - 1 folder)  Folder 2 (one file)


I så fald ser PowerShell Folder1 som tom og forsøger at slette den. Årsagen til, at disse puslespil er mig, fordi hvis jeg højreklikker på Folder1 i Windows Stifinder Det står at Folder1 har 1 mappe og 1 fil i den. Uanset hvad der bruges til at beregne barnets objekter under Folder1 fra Explorer, kan det se barnebarnsobjekter ad infinitum.


Spørgsmål:



Hvordan kan jeg få mit script til at betragte en mappe tom, hvis den har filer som børnebørn eller derover?

Bedste reference


Opdatering til rekursiv sletning:


Du kan bruge en indlejret pipeline som nedenfor:


dir -recurse | Where {$\_.PSIsContainer -and `
@(dir -Lit $\_.Fullname -r | Where {!$\_.PSIsContainer}).Length -eq 0} |
Remove-Item -recurse -whatif


(herfra - Sådan slettes tomme undermapper med PowerShell?)





Tilføj også en ($\_.GetDirectories().Count -eq 0) betingelse:


dir path -recurse | Where-Object { $\_.PSIsContainer } | Where-Object { ($\_.GetFiles().Count -eq 0) -and ($\_.GetDirectories().Count -eq 0) } | Remove-Item


Her er en mere kortfattet måde at gøre dette på:


dir path -recurse | where {!@(dir -force $\_.fullname)} | rm -whatif


Bemærk at du ikke har brug for Foreach-Object, mens du fjerner elementet. Tilføj også en -whatif til Remove-Item for at se, om det vil gøre, hvad du forventer det.

Andre referencer 1


Her er en rekursiv funktion, jeg brugte i et nyere script ...


function DeleteEmptyDirectories {
  param([string] $root)

  [System.IO.Directory]::GetDirectories("$root") |
    \% {
      DeleteEmptyDirectories "$\_";
      if ([System.IO.Directory]::GetFileSystemEntries("$\_").Length -eq 0) {
        Write-Output "Removing $\_";
        Remove-Item -Force "$\_";
      }
    };
}

DeleteEmptyDirectories "P:Path	owherever";

Andre referencer 2


Der var nogle problemer med at lave dette script, en af ​​dem bruger dette til at kontrollere, om en mappe er tom:


{!$\_.PSIsContainer}).Length -eq 0


Jeg opdagede dog, at tomme mapper ikke er dimensioneret med 0, men snarere NULL. Det følgende er PowerShell-scriptet, som jeg vil bruge. Det er ikke mit eget. Det er snarere fra PowerShell MVP Richard Siddaway. Du kan se den tråd, som denne funktion kommer fra over på denne tråd på PowerShell.com. [16] [17]


function remove-emptyfolder {
 param ($folder)

 foreach ($subfolder in $folder.SubFolders){

 $notempty = $false
 if (($subfolder.Files | Measure-Object).Count -gt 0){$notempty = $true}
 if (($subFolders.SubFolders  | Measure-Object).Count -gt 0){$notempty = $true}
 if ($subfolder.Size -eq 0 -and !$notempty){
   Remove-Item -Path $($subfolder.Path) -Force -WhatIf
 }
 else {
  remove-emptyfolder $subfolder
 }

}

}

$path = "c:	est"
$fso = New-Object -ComObject "Scripting.FileSystemObject"

$folder = $fso.GetFolder($path)
remove-emptyfolder $folder

Andre referencer 3


Du kan bruge en rekursiv funktion til dette. Jeg har faktisk allerede skrevet en:


cls

$dir = "C:MyFolder"

Function RecurseDelete()
{
    param   (
            [string]$MyDir
            )

    IF (!(Get-ChildItem -Recurse $mydir | Where-Object {$\_.length -ne $null}))
        {
            Write-Host "Deleting $mydir"
            Remove-Item -Recurse $mydir
        }
    ELSEIF (Get-ChildItem $mydir | Where-Object {$\_.length -eq $null})
        {
            ForEach ($sub in (Get-ChildItem $mydir | Where-Object {$\_.length -eq $null}))
            {
                Write-Host "Checking $($sub.fullname)"
                RecurseDelete $sub.fullname
            }   
        }
    ELSE
        {
            IF (!(Get-ChildItem $mydir))
                {
                    Write-Host "Deleting $mydir"
                    Remove-Item $mydir
                }

        }
}

IF (Test-Path $dir) {RecurseDelete $dir}