killtec
Goto Top

Ordnergrößen mittels Powershell ermitteln

Hallo zusammen,
ich würde gerne folgendes mit Hilfe der Powershell lösen.
Ich habe auf einem Server einen Ordner, nennen wir ihn "basisordner" und diesem sind mehrere unterordner mit wiederum unterordnern.
Ich möchte nun mittels Powershell alle Unterordner unter Basisordner auflisten und deren Größe ermitteln.

Bsp.:
Basisordner
\Unterordner 1
-\Unterordner 11
\Unterordner 2
\Unterordner 3
-\Unterordner 31
-\Unterordner 32

Als Ergebnis soll rauskommen:
Unterordner 1 1GB
Unterordner 2 500 MB
Unterordner 3 3GB

Ich denke mal dass das vorgehen so wäre, erst alle Ordner aufzulisten und dann die Dateigrößen aller Ordner (bzw. unterordner) zu summieren.
Die Ausgabe als TXT / CSV wäre ideal.

Gruß

Edit: Habe da etwas gefunden: http://technet.microsoft.com/en-us/library/ff730945
als txt kann ich das mittels Scriptaufruf und Ausgabeumleitung machen. Lässt sich dies denn noch eleganter lösen?

Edit2: Ich bekomme oft die Meldung, dass der Dateipfad zu lang ist face-sad

Content-Key: 221755

Url: https://administrator.de/contentid/221755

Printed on: April 24, 2024 at 05:04 o'clock

Member: colinardo
colinardo Nov 12, 2013 updated at 14:39:08 (UTC)
Goto Top
Hallo killtec,
Edit2: Ich bekomme oft die Meldung, dass der Dateipfad zu lang ist face-sad
das mit den überlangen Pfaden größer 248 Zeichen ist schon Lange ein Problem von .Net:
http://social.technet.microsoft.com/Forums/scriptcenter/en-US/dedfd9e2- ...

Grüße Uwe
Member: colinardo
colinardo Nov 12, 2013, updated at Nov 14, 2013 at 07:53:15 (UTC)
Goto Top
Ohne jetzt das Problem mit den überlangen Pfaden zu beachten(experimentiere noch an einer Lösung) kannst du die Größen der Ordner nach Größe sortiert folgendermaßen in eine CSV-Datei exportieren:
function parseFolders($sFolder){
    $allFolders = get-childitem $sFolder -ErrorAction SilentlyContinue |?{$_.PSIsContainer}    
    foreach ($f in $allFolders){
        $size = (get-childitem $f.FullName -ErrorAction SilentlyContinue | ?{!($_.PSIsContainer)} | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
        $folderSum += $size
        parseFolders $f.FullName
        if ($f.Parent.FullName -eq $global:startFolder){
            echo "$($f.FullName) hat eine Größe von $([MATH]::Round($folderSum / (1024*1024),2)) MB"  
            $global:folderList += New-Object PSObject -Property @{"Ordner"=$f.FullName;"Größe"="$([MATH]::Round($folderSum / (1024*1024),2)) MB"}  
            $folderSum = 0    
        }
    }

}

$global:startFolder = "C:\temp\source"  
$csvDatei = "C:\temp\test.csv"  
$global:folderList = @()
parseFolders $startFolder
$global:folderList | select Ordner,Größe | Sort-Object -Property Größe -Descending | Export-Csv -Path $csvDatei -Delimiter ";" -NoTypeInformation -Encoding UTF8  
Zeile 16 und 17 anpassen.

Grüße Uwe
Member: killtec
killtec Nov 14, 2013 at 07:12:16 (UTC)
Goto Top
Hi Uwe,
irgendwie mag das Script nicht so recht. Die csv ist 1KB groß und leer face-sad
Habe lange Dateipfade da zwischen und auch Sonderzeichen (deutsche Sonderzeichen). Ist das evtl. ein Problem?

Gruß
Member: colinardo
colinardo Nov 14, 2013 updated at 08:05:56 (UTC)
Goto Top
Dann eliminiere zuerst die langen Pfade.
Um zu ermitteln welche Pfade mehr wie 248 Zeichen haben, kannst du folgende Funktion nutzen:
(Den Startpfad gibst du in der letzten Zeile des Scripts ein)
Function Get-LongPaths {

param(
    [CmdletBinding()]
    [Parameter(
    Position=0, 
    Mandatory=$true, 
    ValueFromPipeline=$true,
    ValueFromPipelineByPropertyName=$true)]
    [string[]]
        $FolderPath,
    [ValidateRange(10,248)]
	[int]
		$MaxChars=248
)

    begin {
        if (!(Test-Path -Path $(Join-Path $env:SystemRoot 'System32\robocopy.exe'))) {  
            write-warning "Robocopy wurde nicht gefunden, bitte installieren Sie Robocopy um diese Funktion nutzen zu können!"  
            return
        }
    }

    process {
        $regex = [Regex] "\t"  
        foreach ($Path in $FolderPath) {
            $RoboOutput = robocopy.exe $Path c:\temp /l /e /b /np /fp /njh /njs /r:0 /w:0
 
            $RoboOutput | Where-Object {$_} |
            ForEach-Object {
                $CurrentPath =  $regex.Split($_)[2]
                if ($CurrentPath.Length -gt $MaxChars) {
                    New-Object -TypeName PSCustomObject -Property @{
                        FullName = $CurrentPath
                        PfadLänge = $CurrentPath.Length
                        Type = if ($CurrentPath.SubString($CurrentPath.Length-1,1) -eq '\') {  
                                   'Ordner'  
                               } else {
                                   'Datei'  
                               }
                    }
                }
            }
        }
    }
}

Get-LongPaths -FolderPath 'C:\Startpfad'  
Die Funktion entstammt im Ursprung von hier, wurde aber von mir verbessert. Sie benötigt als Voraussetzung das 'robocopy' installiert ist.

Es ist ein Witz das MS den Umstand mit den langen Pfaden noch nicht mal mit PS4.0 bzw. Net 4.5 behoben hat...

Grüße Uwe
Member: killtec
killtec Nov 15, 2013 at 09:20:35 (UTC)
Goto Top
Hi Uwe,
sorry, hat etwas gedauert mit dem testen,
ist es richtig, dass die Asugabe leer bleibt? In Zeile 27 hast du noch das Verzeichnis c:\temp stehen. das wäre dann das Zeil korrekt?
Wird dies nur als temporär genutzt, bzw. nur dazu, dass ein Verzeichnis da steht? Das Verzeichnis gibt es bei mir z.b. nicht.

Gruß
Member: colinardo
colinardo Nov 15, 2013 at 09:26:21 (UTC)
Goto Top
Zitat von @killtec:
ist es richtig, dass die Asugabe leer bleibt? In Zeile 27 hast du noch das Verzeichnis c:\temp stehen. das wäre dann das Zeil
korrekt?
Wird dies nur als temporär genutzt, bzw. nur dazu, dass ein Verzeichnis da steht? Das Verzeichnis gibt es bei mir z.b.
nicht.
Das Verzeichnis C:\temp ist nur ein Dummy für Robocopy es wird nicht erstellt bzw. muss nicht existieren.
Wenn die Ausgabe leer ist, sind in dem Ordner und Unterordnern keine überlangen Ordnernamen.
Hier geht diese Überprüfung einwandfrei (PS3.0 Win7x64).
Member: killtec
killtec Nov 15, 2013 at 10:03:37 (UTC)
Goto Top
Ok,
habe das jetzt mal direkt auf meinem PC laufen lassen (also das Script mit den Ordnergrößen).
Ich bkomme folgende Meldung:
PS C:\> .\foldersize_script.ps1
Skriptfehler aufgrund eines Überlaufs der Aufruftiefe. Die Aufruftiefe hat 1001
 erreicht. Der Höchstwert lautet 1000.
    + CategoryInfo          : InvalidOperation: (1001:Int32) , ParentContain
   sErrorRecordException
    + FullyQualifiedErrorId : CallDepthOverflow
Diese Meldung sagt mir nicht so wirklich etwas. Außer, dass evtl. zu vile Aufrufe gemacht wurden?

Gruß
Member: Tsunami87
Tsunami87 Feb 17, 2014 updated at 14:52:38 (UTC)
Goto Top
Hallo,

ich setz mal hier an.
Da die obigen Powershell-Befehle auf Fehler laufen.(Die Aufruftiefe hat 1001erreicht. Der Höchstwert lautet 1000.)

Sind diese an den Start gekommen:
$Content = Get-Content 'c:\Schreiben\Content2.txt'  
foreach($Content in $content)
{$colItems = (Get-ChildItem $content -recurse | Measure-Object -property length -sum)
"$content" + " " + "{0:N2}" -f ($colItems.sum / 1MB) +  " MB"   
}
In Content2.txt stehen ca.100 Einträge.
Dauer ca. 25min.

Gibt es paar Kniffe die Abfrage zu beschleunigen?


Über Tips und Vorschläge wäre ich sehr dankbar.

Grüße
René
Member: colinardo
colinardo Feb 17, 2014 updated at 16:24:23 (UTC)
Goto Top
tach auch,
du verwendest zwei gleich lautende Variablen in deinem ForEach() Konstrukt: $Content und $content das solltest du unbedingt vermeiden und eine separate Variable verwenden:
$Content = Get-Content 'c:\Schreiben\Content2.txt'  
foreach($line in $content){
  $colItems = (Get-ChildItem $line -recurse | Measure-Object -property length -sum)
  "$line" + " " + "{0:N2}" -f ($colItems.sum / 1MB) +  " MB"   
}
ansonsten nutze mal testweise u.s. Code. Denke zwar das es nicht viel schneller sein wird, aber das ist nun mal in Windows so, dass dies seine Zeit dauert. In der Win32 API gibt es auch keine schnellere Methode. Selbst der Windows Explorer benötigt hier eben solange wenn man die Eigenschaften für einen Ordner aufruft der zig tausende Files und Unterordner beinhaltet.
function initCode() {
    [String]$SourceCode = @"  
Namespace colinardo
    Public Class MyFunctions
        Public Shared Function GetDirectorySize(ByVal sPath As String) As Long
            Dim lngSize As Long = 0
            Try
                Dim diDir As New System.io.DirectoryInfo(sPath)
                Dim file As System.io.FileInfo
                For Each file In diDir.GetFiles()
                    lngSize += file.Length
                Next
                Dim subDirInfo As System.io.DirectoryInfo
                For Each subDirInfo In diDir.GetDirectories()
                    lngSize += GetDirectorySize(subDirInfo.FullName)
                Next
            Catch ex as System.Exception
            End Try
            Return lngSize
        End Function
    End Class
End Namespace
"@  
 add-type -TypeDefinition $SourceCode -Language VisualBasic
}
initCode

$Content = Get-Content 'c:\Schreiben\Content2.txt'  
foreach($line in $content){
  "$line" + " " + "{0:N2}" -f ([colinardo.MyFunctions]::GetDirectorySize($line) / 1MB) + " MB"  
}

Grüße Uwe
Member: Tsunami87
Tsunami87 Feb 18, 2014 at 05:52:50 (UTC)
Goto Top
Morgen Uwe,

Danke für den Tipp!

Dein Vorschlag läuft Lokal schneller.
Verwende ich eine Netzwerkresource ist die Geschwindigkeit, gefühlt, gleich.


Grüße
René