2011-08-12 25 views
3

我有一個PowerShell 2.0腳本,我用它來刪除其中沒有任何文件的文件夾:PowerShell的:找到並刪除那些在他們或在孩子沒有文件夾文件夾

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

然而,我注意到,運行腳本時出現了很多錯誤。即:

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

「什麼?!」我驚慌失措。他們應該全部是空的!我只篩選空文件夾!顯然,這不是腳本的工作方式。在這種情況下只具有文件夾作爲孩子,但文件作爲孫子的文件視爲空文件夾:

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

在這種情況下,PowerShell中看到Folder1中爲空,並試圖將其刪除。這使我感到困惑的原因是,如果我在Windows資源管理器中右鍵單擊Folder1它說Folder1中有1個文件夾和1個文件。無論用於從資源管理器中計算Folder1下面的子對象,都可以看到孫子對象無限。

問:

我怎樣才能讓我的腳本沒有考慮的一個文件夾爲空,如果有文件,孫子或超越?

回答

2

有在做這個劇本,其中之一的一些問題正在利用這檢查一個文件夾是否爲空:

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

但是,我發現空文件夾的大小不是0而是NULL。以下是我將使用的PowerShell腳本。這是我自己的不是。相反,它來自PowerShell MVP Richard Siddaway。你可以在this thread on PowerShell.com看到這個函數來自的線程。

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:\test" 
$fso = New-Object -ComObject "Scripting.FileSystemObject" 

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

更新遞歸刪除:

您可以使用嵌套的管道象下面這樣:

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

(從這裏開始 - How to delete empty subfolders with PowerShell?


添加($_.GetDirectories().Count -eq 0)條件太:

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

這裏是這樣做雖然更簡潔的方式:

dir path -recurse | where {[email protected](dir -force $_.fullname)} | rm -whatif 

需要注意的是,在做項目中刪除你不需要的Foreach-Object。也可以在Remove-Item上添加一個-whatif,看看它是否會達到您的預期效果。

+0

啊哈!這就說得通了。但是,我必須反覆運行腳本才能真正清除所有容器,意味着第一次運行會獲得樹上最遠的葉子。然後第二個會得到自上次腳本運行以來現在爲空的下一個集合。等等。那麼,我是不是要循環腳本直到沒有輸出,或者有沒有更好的方法來做到這一點?你願意把它作爲一個完全獨立的問題嗎? – Wesley

+0

@wesley - 你可以使用遞歸函數。檢查我的答案。 – JNK

+0

@WesleyDavid - 您可以嵌套管道。看到我更新的答案和鏈接的答案。 – manojlds

1

您可以對此使用遞歸函數。其實,我已經寫了一個:

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

不知道爲什麼你被downvoted。 – Wesley

+0

我也是,但它發生了。 – JNK

+0

+1您的解決方案有效,但我想建議一個改進:當文件夾名稱包含方括號時,「Remove-Item」調用失敗(靜默)。修復很簡單;像這樣添加'-LiteralPath'參數:'Remove-Item -Recurse -LiteralPath $ mydir'和'Remove-Item -LiteralPath $ mydir'。謝謝。 – Sabuncu

2

這是我在最近的腳本中使用遞歸函數...

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\to\wherever"; 
相關問題