web-dev-qa-db-ja.com

PowerShellですべての空のフォルダーを再帰的に削除するにはどうすればよいですか?

PowerShellで特定のフォルダーのすべての空のフォルダーを再帰的に削除する必要があります(任意のレベルでフォルダーとサブフォルダーを確認します)。

現時点では、このスクリプトを使用していますが成功していません。

修正方法を教えてください。

$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

Windows 8.1バージョンでPowerShellを使用しています。

25
GibboK

これを使用できます:

$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は、フィルタリング後にGet-ChildItemコマンドから返される空のディレクトリの配列です。その後、ループしてアイテムを削除できます。

更新

空のディレクトリを含むディレクトリを削除する場合は、それらがすべてなくなるまでスクリプトを実行し続けるだけです。 $dirsが空になるまでループできます。

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

非表示のファイルとフォルダーも確実に削除する場合は、-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)
31
arco444

このような問題を見るときには、いくつかの重要な点に留意する必要があります。

  1. Get-ChildItem -Recurseは頭部再帰を実行します。つまり、ツリーを歩いているときにフォルダーが見つかるとすぐにフォルダーを返します。空のフォルダーを削除し、空のフォルダーを削除した後にそれらが空の場合は親も削除するため、代わりに末尾再帰を使用する必要があります。これは、フォルダーを最も深い子からルートまで処理します。末尾再帰を使用することにより、空のフォルダーを削除するコードを繰り返し呼び出す必要がなくなります。1回の呼び出しですべてが実行されます。
  2. Get-ChildItemは、既定では非表示のファイルまたはフォルダーを返しません。その結果、空のように見えても非表示のファイルまたはフォルダーを含むフォルダーを削除しないように、追加の手順を実行する必要があります。 Get-ItemとGet-ChildItemの両方には、非表示のファイルまたはフォルダーと可視のファイルまたはフォルダーを取得するために使用できる-Forceパラメーターがあります。

これらの点を念頭に置いて、テール再帰を使用し、隠しファイルまたはフォルダーを適切に追跡し、空の隠しフォルダーを削除し、1つ以上の隠しファイルを含むフォルダーを保持するソリューションを次に示します。

# 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'
33
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
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

ここでの唯一の新しい貢献は、Sort-ObjectディレクトリFullNameによる逆ソート。これにより、親を処理する前に常に子を処理することが保証されます。これにより、空のフォルダーが再帰的に削除されます。

一方で、Select-Object -First 1は、パフォーマンスを大幅に改善するかどうかを決定しますが、改善する可能性があります。

1
Bacon Bits

サブフォルダーを含むフォルダーのみを削除し、それ自体とそのサブフォルダー内のファイルは削除しないようにしたい場合は、これがより簡単で迅速な方法です。

$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

複数のフォルダーの深さにネストされているファイルを削除する場合を除き、コメント/最初の投稿を心に留めません。ファイルを含むディレクトリを含むディレクトリを削除することになります。これの方が良い:

$ FP = "C:\ Temp \"

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

$ Empty = $ dirs | Where-Object {$。GetFiles()。Count -eq 0 -and $。GetDirectories()。Count -eq 0} |

Select-Object FullName

上記のチェックでは、ディレクトリが実際に空であることを確認しますが、OPはファイルがないことを確認するだけです。その結果、ファイルがいくつかのフォルダーの次の深さまで削除されます。

ネストされたDirsを持つDirsは削除されないため、上記を数回実行する必要がある場合があります。そのため、最も深いレベルのみが削除されます。すべてがなくなるまでループします。

私がやらないことは、-forceパラメーターを使用することです。それは仕様です。実際にremove-itemが空ではないディレクトリにヒットした場合、追加の安全性としてプロンプトが表示されます。

0
Zack A

このような何かが私のために働く。このスクリプトは、空のフォルダーおよびフォルダーのみを含むフォルダー(ファイルなし、隠しファイルなし)を削除します。

$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

「Forループ」を使用して、空のサブディレクトリを再帰的に削除することもできます。

始める前に、$ 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

まず、変数$ SBに次のスクリプトブロックを保存します。変数は、&SBコマンドを使用して後で呼び出すことができます。 &SBコマンドは、$ HOME\Desktop\Testに含まれる空のサブディレクトリのリストを出力します

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

注:-Forceパラメーターは非常に重要です。非表示のファイルとサブディレクトリを含むが、それ以外の場合は空であるディレクトリが「Forループ」で削除されないようにします。

「For Loop」を使用して、$ HOME\Desktop\Testの空のサブディレクトリを再帰的に削除します

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

PowerShell 4.0での動作としてテスト済み

0
Zelda64

対象の親フォルダー内にいると仮定します

gci . -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }

_$tdc_の場合、次のようになります

gci $tdc -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }

0
DeepSpace101

RichardHowellsの脚本を改作しました。 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