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
윈도우즈 8.1 버전에서 PowerShell을 사용하고 있습니다.
다음과 같은 문제를 검토할 때 몇 가지 핵심 사항을 염두에 두어야 합니다.
Get-ChildItem -Recurse
에서는 머리 재귀를 수행합니다. 즉, 트리를 통과할 때 폴더를 찾으면 즉시 폴더를 반환합니다.빈 폴더를 제거하고 빈 폴더를 제거한 후 빈 폴더의 상위 폴더도 제거하려면 가장 깊은 하위 폴더에서 루트까지 폴더를 처리하는 꼬리 재귀를 대신 사용해야 합니다.꼬리 재귀를 사용하면 빈 폴더를 제거하는 코드를 반복적으로 호출할 필요가 없습니다. 한 번의 호출로 모든 작업을 수행할 수 있습니다.Get-ChildItem
은 기본적으로 숨겨진 파일 또는 폴더를 반환하지 않습니다.따라서 비어 있는 것처럼 보이지만 숨겨진 파일 또는 폴더가 포함된 폴더를 제거하지 않도록 추가 단계를 수행해야 합니다.Get-Item
그리고.Get-ChildItem
둘 다가 있습니다.-Force
숨겨진 파일이나 폴더뿐만 아니라 표시되는 파일이나 폴더를 검색하는 데 사용할 수 있는 매개 변수입니다.
이러한 점을 고려하여, 숨겨진 파일이나 폴더를 올바르게 추적하고, 숨겨진 폴더가 비어 있는 경우 해당 폴더를 제거하고, 하나 이상의 숨겨진 파일을 포함할 수 있는 폴더를 보관하는 솔루션이 있습니다.
먼저 이 작업을 수행하는 스크립트 블록(익명 함수)입니다.
# A script block (anonymous function) 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
}
}
테스트하려면 흥미로운 테스트 데이터를 생성하는 코드가 있습니다(삭제되므로 c:\a 폴더가 없는지 확인하십시오).
# This creates 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
사용 방법은 다음과 같습니다. 맨 위 됩니다(" 제거니됩다폴가더상하단면)가 제거됩니다.C:\a
이 예제의 폴더. 위 스크립트를 사용하여 테스트 데이터를 생성한 경우 생성됨) 아래의 모든 빈 폴더를 삭제한 후 해당 폴더가 비어 있는 경우 생성됩니다.
& $tailRecursion -Path 'C:\a'
다음을 사용할 수 있습니다.
$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)
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
디렉터리의 전체 이름을 기준으로 역순 정렬합니다.이렇게 하면 부모를 처리하기 전에 항상 자녀를 처리할 수 있습니다(즉, 커크 먼로의 답변에 설명된 "꼬리 재귀").이렇게 하면 빈 폴더를 재귀적으로 제거할 수 있습니다.
지금 당장, 나는 확신할 수 없습니다.Select-Object -First 1
성능이 의미 있게 향상되든 그렇지 않든 간에 가능합니다.
제가 여기서 이미 긴 답변 목록에 기여할 것이라고 생각했습니다.
대부분의 답변은 한 번 이상 실행해야 하는 것과 같은 특이한 점이 있습니다.중복 검색을 방지하기 위해 꼬리 재귀를 사용하는 등 일반 사용자에게 지나치게 복잡한 경우도 있습니다.
여기 제가 몇 년 동안 사용해온 아주 간단한 원라이너가 있습니다. 아주 잘 작동합니다.
, 숨진폴파일/더를설명않을 추가하여 할 수 .-Force
에▁Get-ChildItem
다음은 정규화된 긴 cmdlet 이름 버전입니다.
Get-ChildItem -Recurse -Directory | ? { -Not ($_.EnumerateFiles('*',1) | Select-Object -First 1) } | Remove-Item -Recurse
그러니까 기본적으로...다음과 같습니다.
Get-ChildItem -Recurse -Directory
검색 : "Directory"를 찾습니다.$_.EnumerateFiles('*',1)
각 디렉터리에 대해...파일 열거EnumerateFiles
입니다.GetFiles
작업이 완료되면 출력됩니다. 적어도 그렇게 작동해야 합니다. NET에서 로... PowerShell에서 어떤 이유로GetFiles
즉시 뱉기 시작합니다.하지만 여전히 사용합니다.EnumerateFiles
테스트에서 신뢰성 있게 더 빨랐기 때문입니다.('*',1)
모든 파일을 재귀적으로 찾는 것을 의미합니다.
| Select-Object -First 1
발견된 첫 번째 파일에서 중지- 이것이 얼마나 도움이 되는지 테스트하기가 어려웠습니다.어떤 경우에는 엄청나게 도움이 되기도 했고, 어떤 경우에는 전혀 도움이 되지 않았고, 어떤 경우에는 약간씩 속도를 늦추기도 했습니다.그래서 정말 모르겠어요.이것은 선택 사항인 것 같습니다.
| Remove-Item -Recurse
디렉터리를 재귀적으로 제거합니다(빈 하위 디렉터리가 포함된 디렉터리가 제거되는지 확인).
문자를 셀 경우 다음으로 단축할 수 있습니다.
ls -s -ad | ? { -Not ($_.EnumerateFiles('*',1) | select -First 1) } | rm -Recurse
-s
-Recurse
-ad
-Directory
파일이 너무 많지 않아 성능에 신경을 쓰지 않는 경우…. 더욱 그러합니다.
ls -s -ad | ? {!($_.GetFiles('*',1))} | rm -Recurse
참고 사항:이것을 가지고 노는 동안, 저는 다양한 버전을 테스트하기 시작했습니다.Measure-Command
수백만 개의 파일과 수천 개의 디렉터리를 가진 서버에 대해.
이것은 내가 사용해온 명령(위)보다 빠릅니다.
(gi .).EnumerateDirectories('*',1) | ? {-Not $_.EnumerateFiles('*',1) } | rm -Recurse
ls c:\temp -rec |%{ if ($_.PSIsContainer -eq $True) {if ( (ls $_.fullname -rec | measure |select -expand count ) -eq "0" ){ ri $_.fullname -whatif} } }
사용자가 관심 있는 상위 폴더 안에 있다고 가정합니다.
gci . -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }
에 대한 귀하의 경우$tdc
될 것입니다
gci $tdc -Recurse -Directory | % { if(!(gci -Path $_.FullName)) {ri -Force -Recurse $_.FullName} }
하위 폴더를 포함할 수 있고 내부 및 하위 폴더에 파일이 없는 폴더만 삭제하는 것이 더 쉬운 방법일 수 있습니다.
$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}
}
빈 하위 디렉터리를 재귀적으로 제거하려면 "루프용"을 사용할 수도 있습니다.
시작하기 전에 $HOME\Desktop\에서 작업할 하위 디렉터리와 텍스트 파일을 만들어 보겠습니다.시험
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\에 포함된 빈 하위 디렉터리 목록을 출력합니다.시험
$SB = {
Get-ChildItem $HOME\Desktop\Test -Directory -Recurse |
Where-Object {(Get-ChildItem $_.FullName -Force).Count -eq 0}
}
참고: -Force 매개 변수는 매우 중요합니다.숨겨진 파일 및 하위 디렉터리를 포함하지만 비어 있는 디렉터리는 "루프용"에서 삭제되지 않도록 합니다.
이제 "For Loop"을 사용하여 $HOME\Desktop\에서 빈 하위 디렉터리를 재귀적으로 제거합니다.시험
For ($Empty = &$SB ; $Empty -ne $null ; $Empty = &$SB) {Remove-Item (&$SB).FullName}
PowerShell 4.0에서 작동하는 것으로 테스트됨
저는 리처드 하웰스의 대본을 각색했습니다.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
이런 것이 저에게 효과가 있습니다.이 스크립트는 폴더만 포함하는 빈 폴더 및 폴더(파일 없음, 숨김 파일 없음)를 삭제합니다.
$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
}
}
두 개 이상의 폴더에 중첩된 파일도 삭제하지 않는 한 코멘트/첫 번째 게시물을 마음에 새기지 않을 것입니다.파일을 포함할 수 있는 디렉터리를 포함할 수 있는 디렉터리를 삭제하게 됩니다.이것이 더 낫습니다.
$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는 파일이 없는지만 확인합니다.이렇게 하면 폴더 깊이에 있는 다음 파일도 삭제됩니다.
중첩된 Dir가 있는 Dir는 삭제되지 않으므로 위를 몇 번 실행해야 할 수도 있습니다.따라서 가장 깊은 레벨만 삭제됩니다.그래서 그들이 모두 사라질 때까지 반복하세요.
-force 매개 변수를 사용하지 않는 또 다른 방법으로는 -force 매개변수를 사용하는 것입니다.그것은 의도적인 것입니다.실제로 remove-item이 비어 있지 않은 dir에 표시되는 경우 추가 안전 메시지가 표시됩니다.
$files = Get-ChildItem -Path c:\temp -Recurse -Force | where psiscontainer ; [array]::reverse($files)
[Array]::reverse($files)
항목을 되돌리기 때문에 계층 구조에서 가장 낮은 파일을 먼저 얻을 수 있습니다.삭제하기 전에 파일 경로가 너무 긴 파일 이름을 조작하는 데 사용합니다.
의 빈 됩니다.$tdc
또한 여러 번 실행할 필요가 없기 때문에 훨씬 더 빠릅니다.
$tdc = "x:\myfolder" # Specify the root folder
gci $tdc -Directory -Recurse `
| Sort-Object { $_.FullName.Length } -Descending `
| ? { $_.GetFiles().Count -eq 0 } `
| % {
if ($_.GetDirectories().Count -eq 0) {
Write-Host " Removing $($_.FullName)"
$_.Delete()
}
}
#By Mike Mike Costa Rica
$CarpetasVacias = Get-ChildItem -Path $CarpetaVer -Recurse -Force -Directory | Where {(gci $_.fullName).count -eq 0} | select Fullname,Name,LastWriteTime
$TotalCarpetas = $CarpetasVacias.Count
$CountSu = 1
ForEach ($UnaCarpeta in $CarpetasVacias){
$RutaCarp = $UnaCarpeta.Fullname
Remove-Item -Path $RutaCarp -Force -Confirm:$False -ErrorAction Ignore
$testCar = Test-Path $RutaCarp
if($testCar -eq $true){
$Datem = (Get-Date).tostring("MM-dd-yyyy HH:mm:ss")
Write-Host "$Datem ---> $CountSu de $TotalCarpetas Carpetas Error Borrando Directory: $RutaCarp" -foregroundcolor "red"
}else{
$Datem = (Get-Date).tostring("MM-dd-yyyy HH:mm:ss")
Write-Host "$Datem ---> $CountSu de $TotalCarpetas Carpetas Correcto Borrando Directory: $RutaCarp" -foregroundcolor "gree"
}
$CountSu += 1
}
이는 간단한 접근 방식입니다.
dir -Directory | ? { (dir $_).Count -eq 0 } | Remove-Item
언급URL : https://stackoverflow.com/questions/28631419/how-to-recursively-remove-all-empty-folders-in-powershell
'programing' 카테고리의 다른 글
어레이의 정확한 복제 사본을 만들려면 어떻게 해야 합니까? (0) | 2023.07.31 |
---|---|
Android에서 ProgressBar의 진행률 표시기 색상을 변경하는 방법 (0) | 2023.07.31 |
tnsping을 설치하는 방법은 무엇입니까? (0) | 2023.07.31 |
동적 및 네임스페이스 모듈이 등록되지 않았습니다(vuex-module-decorator, vuex-class). (0) | 2023.07.31 |
여러 테이블에 여러 개의 FULL OUTER JOIN(전체 외부 조인 (0) | 2023.07.31 |