programing

PowerShell에서 모든 빈 폴더를 재귀적으로 제거하는 방법은 무엇입니까?

lastmoon 2023. 7. 31. 21:50
반응형

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을 사용하고 있습니다.

다음과 같은 문제를 검토할 때 몇 가지 핵심 사항을 염두에 두어야 합니다.

  1. Get-ChildItem -Recurse에서는 머리 재귀를 수행합니다. 즉, 트리를 통과할 때 폴더를 찾으면 즉시 폴더를 반환합니다.빈 폴더를 제거하고 빈 폴더를 제거한 후 빈 폴더의 상위 폴더도 제거하려면 가장 깊은 하위 폴더에서 루트까지 폴더를 처리하는 꼬리 재귀를 대신 사용해야 합니다.꼬리 재귀를 사용하면 빈 폴더를 제거하는 코드를 반복적으로 호출할 필요가 없습니다. 한 번의 호출로 모든 작업을 수행할 수 있습니다.
  2. 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

반응형