.NET: Sehr schnelle Datei Enumeration im großen Stil | Alternativ zu EnumerateFileSystemEntries

DPXone

Lieutenant
Registriert
Mai 2009
Beiträge
554
Hi Zusammen,

mein Problem, das mich schon länger beschäftigt, ist, wie kann ich sehr schnell (instantly) alle Dateien in allen Unterordneren, ausgehend von einem Root-Folder, auflisten, ohne jegliche Datei-Attribute abzufragen.
Mich interessieren erstmal nur Dateinamen / die Existenz von Dateien.

Die beste Lösung wäre ja EnumerateFileSystemEntries aus der Klasse System.IO.Directory.
Problem:
Sobald man auf einen Ordner oder eine Datei keinen Zugriff hat (sei es auch durch die UAC), endet der Prozess und man erhält nur bis dahin zugreifbare Dateien.
Man hat bei dieser Methode einfach keine Möglichkeit diese Exception zu ignorieren.
Ein Ignore Parameter wäre hier halt Optimal, weil diese Methode von der Performance her einfach das Ziel ist.

Ein Abfragen jedes Ordners und jeder Datei einzeln zieht die Dauer der Abfrage einfach enorm in die Höhe.
Selbst wenn man mit der SearchOption TopDirectoryOnly jeden Ordner rekrusiv durchgeht, könnte es ja mal vorkommen, dass eine bestimmte Datei nicht die Berechtigungen des Ordners vererbt bekommen hat. -> ENDE

Wollte deshalb mal wissen, ob jemand eine Möglichkeit hat hier direkt die Master File Table abzufragen (sofern das die schnellste Möglichkeit ist) und hierbei Exceptions ignorieren kann.

Gruß
Sven
 
Danke für die schnelle Antwort.
Aber folgende Bedingung scheint nicht in meine Lösung zu passen:
If you take each of these in turn, asserting sufficient privileges is the most obscure part. There's a Windows API to change the privileges of the running token - and you use that to add the necessary privileges. Here's an excerpt from a class that I use to assert those privileges. You could assert a bunch more privileges - but this should be sufficient for reading the MFT.

Your application will need to run under an account that can actually obtain the requisite privileges - an admin account is good. Also, a backup operator will work.
Geht wieder um Berechtigungen.
Würde das schon gern im User-Kontext ausführen, ohne Admin-Rechte.

Die zweite Variante muss ich mir noch anschauen.
 
Ja, vielleicht funktioniert #2 ohne spezielle Rechte.

Aber man kann das Programm ja als spezieller User, welcher genau nur diese Berechtigungen hat (Backup-Operator-Rolle, oder was auch immer benötigt wird) ausführen.
 
Wenns plattformunabhängig sein soll ist imho ein rekursives Directory.EnumerateFiles / EnumerateDirectories schon der richtige Weg.

DPXone schrieb:
Selbst wenn man mit der SearchOption TopDirectoryOnly jeden Ordner rekrusiv durchgeht, könnte es ja mal vorkommen, dass eine bestimmte Datei nicht die Berechtigungen des Ordners vererbt bekommen hat. -> ENDE
Wenn du für einen Ordner die List Folder permission hast, kannst du alle FileSystemEntries in dem Ordner ohne Probleme auflisten. Die Berechtigungen der jeweiligen Dateien/Unterordner sind irrelevant.

Ansonsten: https://stackoverflow.com/questions/26321366/fastest-way-to-get-directory-data-in-net
 
So. Nach langer Zeit hab ich mich nochmal hingehockt.
Glaub jetzt hab ich mal den richtigen Ansatz.

Ziel war es ja, nur Dateien und Ordner als Pfad (String) zurückzugeben, ohne diverse Attribute abzufragen.
Hat und hatte bei mir verschiedene Verwendungszwecke.
Beispiel: Man will gewisse Dateien anhand des Dateinamens einlesen. Da sind alle Attribute total egal und verzögern nur die Ausführung (besonders bei Pfaden mit tausenden+ von Dateien)

Habe mir dazu ein CMDlet in C# gebaut, welches die Fehler von [System.IO.Directory]::EnumerateFileSystemEntries abfängt und weiter läuft und lediglich einen Write-Error auswirft, welche durch "-ErrorAction Ignore" auch unterdrückt werden kann.

Das hab ich nun relativ gut über mein CMDlet Get-FileSystemEntry gelöst.

Hier das C# Projekt für Visual Studio + DLL auf OneDrive:
https://1drv.ms/u/s!AkQAXCpcXqgZgu4eWlBrjqNjMww1yA?e=pkxuAJ
Den Source-Code als Plain-Text findet ihr auch unten.

Falls man noch was performance-mäßig verbessern kann, dann bitte immer her damit ;)
Einige Basics inkl. Help für CMDlet's im Code fehlen noch.

Zur Info:
Anfangs wollte ich alles als "yield" zurückgeben, aber das hat sich dann im Nachhinein in PowerShell als etwas problematisch erwiesen.
Richtig problematisch wurde allerdings die zweimalige Ausgabe der Fehlermeldung für UnauthorizedAccessException abzufangen, da ich nicht weiß wie ich mit "yield" einen Fehler an die aufrufende Funktion übergeben kann.

Alle bisherigen Versuche das direkt in der Methode "GetFileSystemEntries" abzufangen, scheiterten damit, dass auch PowerShell direkt ab diesem Zeitpunkt jegliche weitere Ausführung abgebrochen hat, ähnlich wie es auch "[System.IO.Directory]::EnumerateFileSystemEntries" macht.

Musste das dann im Nachhinein über eine String-Manipulation (="//ERROR//") hilfsweise lösen. Falls dazu noch jemand eine Alternative weiß, wäre ich sehr dankbar.

Sonstiges:
Die Einschränkung auf Dateien, Ordner oder beides lässt sich über den PowerShell-Parameter -ItemType regeln.

Test-Script:
PowerShell:
CLS 

import-module "$env:USERPROFILE\OneDrive\Windows\Powershell\Modules\DPXone.FileSystem.dll"   # change path to downloaded DLL

$Dir = 'C:\Windows\WinSxS' # Entire nested folder structure must be listable by authenticated users/users, otherwise '[System.IO.Directory]::EnumerateFileSystemEntries' will fail while 'Get-FileSystemEntry' will not!
$maxMeasuringCount = 3 



$t1 = $t2 = $t3 = $t4 = $r1 = $r2 = $r3 = $r4 = $temp = $null  # set variables to $null

Write-Host "$("`r`n" * 5)Measured commands are executed $maxMeasurings times in a row.`r`n" 
For ($measuring = 1 ; $measuring -le $maxMeasuringCount ; $measuring++) { # Measuring $maxMeasuringCount times
	write-progress -Activity 'Measurings running ...' -PercentComplete($measuring / $maxMeasuringCount * 100) 
	Write-Host "Measuring $measuring`:" 
	
	# Get-FileSystemEntry       
	$t1 = Measure-Command { $r1 = Get-FileSystemEntry -Path $Dir -Recurse } # yielded returns are directly returned by the CMDlet trough a 'ForEeach'-statement       
	write-host ("`t{0,-50}: {1} for {2:n0} items" -f 'Get-FileSystemEntry' , $t1.ToString() , $r1.Count) 
	
	# Get-ChildItem      
	$t2 = Measure-Command { $r2 = Get-ChildItem -Path $Dir -Recurse -Force } 
	write-host ("`t{0,-50}: {1} for {2:n0} items" -f 'Get-ChildItem' , $t2.ToString() , $r2.Count) 
	
	# EnumerateFileSystemEntries non-yielded return      
	$t3 = Measure-Command { 
		$temp = [System.IO.Directory]::EnumerateFileSystemEntries($Dir , '*' ,[System.IO.SearchOption]::AllDirectories) # yielded returns. For real execution time, collection must run through a 'ForEach'-statement.       
		$r3 = Foreach ($item In $temp) { 
			$item 
		} 
	} 
	write-host ("`t{0,-50}: {1} for {2:n0} items" -f 'EnumerateFileSystemEntries' , $t3.ToString() , $r3.Count) 
	
	# EnumerateFileSystemEntries yielded return without count     
	$t4 = Measure-Command { $r4 = [System.IO.Directory]::EnumerateFileSystemEntries($Dir , '*' ,[System.IO.SearchOption]::AllDirectories) } # yielded returns.  
	write-host ("`t{0,-50}: {1} for {2:n0} items" -f 'EnumerateFileSystemEntries (yielded)' , $t4.ToString() , 'N/A') 
}


Das Ergebnis:
PowerShell:
Measured commands are executed 3 times in a row.

Measuring 1:
	Get-FileSystemEntry                               : 00:00:04.8323703 for 90.170 items
	Get-ChildItem                                     : 00:00:12.8026912 for 90.170 items
	EnumerateFileSystemEntries                        : 00:00:04.3718143 for 90.170 items
	EnumerateFileSystemEntries (yielded)              : 00:00:00.0004223 for N/A items
Measuring 2:
	Get-FileSystemEntry                               : 00:00:04.6621670 for 90.170 items
	Get-ChildItem                                     : 00:00:13.7106685 for 90.170 items
	EnumerateFileSystemEntries                        : 00:00:04.5423552 for 90.170 items
	EnumerateFileSystemEntries (yielded)              : 00:00:00.0001479 for N/A items
Measuring 3:
	Get-FileSystemEntry                               : 00:00:04.8208654 for 90.170 items
	Get-ChildItem                                     : 00:00:14.4663573 for 90.170 items
	EnumerateFileSystemEntries                        : 00:00:04.5735562 for 90.170 items
	EnumerateFileSystemEntries (yielded)              : 00:00:00.0001662 for N/A items


Source-Code C#:
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;

namespace DPXone.FileSystem {

    [Cmdlet(VerbsCommon.Get, "FileSystemEntry")]
    [OutputType(typeof(string))]
    public class GetFileSystemEntryCmdlet : Cmdlet {

        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
        public string[] Path { get; set; }

        [Parameter]
        public SwitchParameter Recurse { get; set; } = false;

        [Parameter]
        public UInt32 Depth { get; set; } = 0;

        [Parameter]
        public string Filter { get; set; } = "*";

        [Parameter]
        public FSItemType ItemType { get; set; } = FSItemType.FileSystemEntry;

        protected override void BeginProcessing() {
            base.BeginProcessing();
            if (Depth == 0 && Recurse) {
                Depth = UInt32.MaxValue;
            }
        }

        protected override void ProcessRecord() {
            foreach (string PathItem in Path) {
                IEnumerable<string> items = Search(PathItem, Filter, ItemType, 0, Depth);
                foreach (string item in items) {
                    WriteObject(item);
                }
            }
        }

        private IEnumerable<string> Search(string searchPath, string searchPattern, FSItemType itemType, UInt32 currentDepth, UInt32 maxDepth) {
            IEnumerable<string> fileSystemEntries = GetFileSystemEntries(searchPath, searchPattern, itemType);
            
            foreach (string entry in fileSystemEntries) {
                if (entry == "//ERROR//") { // avoid further processing of folders throwing UnauthorizedAccessException, otherwise errors to console are written twice.
                    yield break;
                }
                yield return entry;
            }

            currentDepth++;
            if (currentDepth >= maxDepth) { yield break; }
            IEnumerable<string> directories = GetFileSystemEntries(searchPath, "*", FSItemType.Directory);

            foreach (string directory in directories) {
                fileSystemEntries = Search(directory, searchPattern, itemType, currentDepth, maxDepth);

                foreach (string entry in fileSystemEntries) {
                    yield return entry;
                }
            }
        }

        private IEnumerable<string> GetFileSystemEntries(string path, string searchPattern, FSItemType itemType) {
            IEnumerable<string> fileSystemEntries = null;
            bool hasAccessError = false;
            try {
                switch (itemType) {
                    case FSItemType.FileSystemEntry:
                        fileSystemEntries = Directory.EnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly);
                         break;
                    case FSItemType.File:
                        fileSystemEntries = Directory.EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
                         break;

                    case FSItemType.Directory:
                        fileSystemEntries = Directory.EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
                        break;
                }
            } catch (UnauthorizedAccessException ex) {
                ErrorRecord err = new ErrorRecord(ex, "FSItemUnauthorizedAccessError", ErrorCategory.PermissionDenied, path);
                WriteError(err);
                hasAccessError = true;
               
            } catch (Exception ex) {
                ErrorRecord err = new ErrorRecord(ex, "NotSpecified", ErrorCategory.NotSpecified, path);
                WriteError(err);
            }

            if (!hasAccessError) { // avoid further processing of folders throwing UnauthorizedAccessException, otherwise errors to console are written twice.
                foreach (string entry in fileSystemEntries) {
                    yield return entry;
                }
            }else {
                yield return "//ERROR//";
            }
        }

        public enum FSItemType {
            FileSystemEntry,
            File,
            Directory
        }
    }
}
 
Zuletzt bearbeitet:
Wie jetzt, abgebrochen? :confused_alt:
Ich muß mal so blöd fragen: weißt Du, was yield macht?

PS kann mit yield nichts anfangen, das ist richtig, aber muß es auch nicht, wenn besagtes yield in einem cmdlet (binär) steckt.

Wobei ich irgendwie grad bissel auf dem Schlauch zu stehen scheine - werd grad aus dem Code nicht recht schlau.

Je nachdem wieviel Zeit und Lust Du hast, könntest Du das Ganze testweise in LINQ implementieren (binär, versteht sich; PS kennt kein LINQ und keine Extensions). Damit wäre schonmal was gewonnen, denn yield macht händisch das, was LINQ als Framework macht, und wenn LINQ schneller ist als yield, dann zickt noch irgendwo was.

Normal sollte es mit PS und yield keine Probleme geben AUßER Du versuchst yield in ein PS-Script einzubauen. Das funktioniert tatsächlich nicht.

Aber sowas wie eine binäres cmdlet Get-NextFile, wo Du einfach sowas wie
C#:
foreach(FileInfo f in ... ) yield return f.FullName;
drin hast, sollte erwartungsgemäß mit jedem Aufruf einen (exakt einen!) anderen Dateinamen liefern.

Naja, zumindest dann, wenn die Enumeration aus FileNames irgendwie statisch ist.
 
RalphS schrieb:
Ich muß mal so blöd fragen: weißt Du, was yield macht?
Anfangs nicht. Aber mittlerweile glaube ich, ich hab das Prinzip verstanden ;) .
Die "on-item-execution-processing" (so nenn ich das jetzt mal) Variante ist schon praktisch.
Nur durch tatsächlich Nutzung eines Items der IEnumerable-Collection wird verarbeitet.

Aber wie schon gesagt, ich dachte mit yield komm ich DIREKT ans Ziel.
Zudem wollte ich halt die Ausführungs-Zeit von "EnumerateFileSystemEntries", "EnumerateFiles" und "EnumerateDirectories" nicht massiv überschreiten.

Das habe ich ja mit obigen Beispiel auch geschafft.
Die Zeiten meines CMDlets verglichen mit "EnumerateFileSystemEntries" sind nahezu identisch.

Einzigstes Problem bestand darin, dass ich Fehler in der Methode "GetFileSystemEntries" nicht an die Methode "Search" zurückgeben konnte, ohne dass die komplette Ausführung abbrach, trotz "Try..Catch" in "Search".

Was genau ist denn nicht verständlich im Code, damit ich besser drauf eingehen kann?

PS:
Zudem hat PowerShell bei 100% yield-processing im CMDlet die Ausgabe extrem verzögert, wenn in PowerShell mit ForEach weitergearbeitet wurd
Ein direkter Abbruch war unmöglich.
PowerShell lief weiter bis alle yielded returns verarbeitet wurden.
 
Zuletzt bearbeitet:
Hehe, wie gesagt, ich glaub ich steh etwas auf dem Schlauch. Vielleicht liegt es an Dir und Deinem Code -- wahrscheinlicher ist aber, daß ich einfach nen schlechten Tag erwischt hab. :daumen:

Was mich nachdenken läßt, ist, daß ich vor einiger Zeit ein ähnliches Problem hatte. Da ging es darum, Logfiles auszuwerten. Dafür wollte ich ein kleines PS-Script haben, weil die Logfiles selber einfach nur undurchsichtig sind, besonders, wenn die auf etwa 20'000 Zeilen/Tag auflaufen.

Das hab ich mit yield erledigt, und das Ergebnis war, daß nicht mehr O(n) Laufzeit war, sondern O(1). Mit Zuweisung wurde eben nicht die gesamte Enumeration durchgegangen und kopiert, sondern -- wie es modern so schön heißt -- es wurde ein Promise geliefert welches nicht zum Zuweisungszeitpunkt, sondern zum Abfragezeitpunkt aufgelöst wurde.

Was dazu führte, daß eine Logfile binnen Momenten verarbeitet werden konnte.

Aber, Einschränkung: es ging eben NICHT darum, alle diese Ergebnisse auf die Ausgabe zu werfen. Der Kram muß analysier- und filterbar sein, und an der Stelle hilft yield, weil nicht alle 20'000 Einträge durchlaufen werden, sondern nur die, für die der Filter paßt.

Die von Dir beschriebenen Probleme konnte ich aber nicht feststellen - hab nicht mal gemerkt, daß das ein Problem sein könnte, aber das kann natürlich dem Use Case geschuldet sein.

Gib mir mal bis über die Feiertage, ich guck in der Zwischenzeit mal drüber und versuch das mal nachzustellen. Aber im Vorhinein der Hinweis: wenn Du nichts filterst, dann nützt Dir auch yield nur bedingt, weil die gesamte "normal" erforderliche Ausführungszeit weiterhin erforderlich bleibt, nur nach weiter hinten verschoben wird.
 
Hehe. Ja das kenn ich :D

RalphS schrieb:
Aber im Vorhinein der Hinweis: wenn Du nichts filterst, dann nützt Dir auch yield nur bedingt, weil die gesamte "normal" erforderliche Ausführungszeit weiterhin erforderlich bleibt, nur nach weiter hinten verschoben wird.

Die yield-Variante hab ich deshalb weiterhin verfolgt, da die drei Methoden Directory.EnumerateDirectories, Directory.EnumerateFiles und Directory.EnumerateFileSystemEntries eine IEnumerable-Collection via yield zurückgeben. Das kann ich ja nicht beeinflussen, aber dort wird ja auch gefiltert. ( Default: "*")

Mit dem Parameter "-Filter" kann man die Filterung in PowerShell auch ändern.

Danke dir ;)
 
Kleines Update: Hab das cmdlet mal entyieldifiziert und verlinqt. Konnte das zweifach-Problem nachstellen - mit yield hat das nix zu tun; interessanterweise fängt ein try/catch exakt eins.

Mit WriteError hat es auch nichts zu tun, Writewarning macht dasselbe. Ist also offensichtlich ein strukturelles Problem. Muß ich mir nochmal in Ruhe angucken.

Was flink+großer Stil angeht, wie wärs mit paralleler Verarbeitung? Aber, wie schon erwähnt (glaub ich) das allergrößte Problem ist die Ausgabe. Wenn man das in eine Variable schreibt, kein Ding. Wenn nicht und es wird alles auf den Bildschirm geschüttet... dann dauert es, immer.
 
So, kurzer und ganz flinker Test, der aussagekräftig sein könnte oder vielleicht auch nicht.

1. Wie angedeutet wurde das cmdlet überarbeitet und eine ein wenig optimierte LINQ-Variante überführt. Das Teil ist alles andere als optimal - PLINQ wird nicht verwendet -- aber es soll auch erstmal nur der Veranschaulichung bzw der Überprüfbarkeit dienen. Schließlich hab ich hier nicht sehr breite Testmöglichkeiten.

2. Zur Zeitmessung wurde
PowerShell:
$tsStart = [datetime]::now
$payload = Get-FileSystemEntry -Path $ENV:USERPROFILE -Recurse
$tsEnd = [datetime]::now
$tsDelta = $tsEnd - $tsStart
gesagt.

Ergebnis:
$tsDelta => Dauer: 11.75 Sekunden
$payload.count = 305'507

Zur Gegenüberstellung wurde die Zuweisung $payload= für einen weiteren Test ausgelassen und das Delta ermittelt. Ergebnis:
$tsDelta => Dauer: 4 Minuten 38 Sekunden (4'37"985).

Das ist 24mal so lange und führt hoffentlich eindringlich vor Augen, wie teuer die Ausgabe ist. Je mehr man davon hat, desto mehr sollte man davon weglassen bzw. irgendwo wegschreiben, zB eben in eine Variable, wenn man damit weiterarbeiten will.


Implementierung:
Ich hab noch ein paar bells+whistles drangebammelt, kann man machen, muß man aber natürlich nicht, nur ein Indiz darauf, was geht.

Listen aller Art sollten readonly sein, wenn sie Parameter sind. Zuweisung zur Laufzeit muß aber möglich sein. Deshalb sind Listen get; protected set; .

LINQ wurde ein bißchen verschachtelter. Hey, es ist LINQ. :daumen:

Funktionsparameter (in) sind readonly. Keine Ausnahme. Habe deshalb CurrentDepth++ aufgelöst.

Den Rückgabewert kann man weiter filtern, mit LINQ in C#, ggfs mit Hilfe von PLINQ, oder halt auf irgendeine Art in PowerShell.

Die meiste Performance kriegt man, wenn man - wie dargelegt -- auf Ausgaben nach Möglichkeit verzichtet und nur exakt das aufschreiben läßt, was man auch wirklich haben will. Das kann bei PS unter Umständen auch ein Objekt sein - das muß man nur referenzieren und nicht kopieren.

Note: besondere Funktionstests wurden nicht durchgeführt, das Ganze läuft sauber von vorn bis hinten durch, Ausnahmen terminieren nicht. Ich hab sie aber auch nicht gezählt. 😊 Gut möglich, daß es immer noch zwei Meldungen pro Ausnahme sind. Diese können aber, wie angedacht, per -ErrorAction SilentlyContinue ignoriert oder per ... Stop abgefangen werden.

Keine Gewähr, und so weiter. Pures proof-of-concept zur Veranschaulichung. Ob man ohne SearchOption.TopDirectoryOnly vielleicht performanter wegkommt hab ich auch nicht getestet. Das wäre womöglich der trivialste, aber dennoch performantere Ansatz.


C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Automation;
namespace DPXone.FileSystem
{
    [Cmdlet(VerbsCommon.Get, "FileSystemEntry")]
[CmdletBinding(PositionalBinding = false, ConfirmImpact = ConfirmImpact.None)]
[OutputType(typeof(IEnumerable<string>))]
public class GetFileSystemEntryCmdlet : Cmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)]
        public List<string> Path { get; internal set; }
        [Parameter()]
        public SwitchParameter Recurse { get; internal set; }
        [Parameter]
[ValidateRange(0, uint.MaxValue)]
        public uint Depth { get; internal set; } = 0;
        [Parameter]
[SupportsWildcards()]
        public string Filter { get; internal set; } = "*";
        [Parameter()]
        public FSItemType ItemType { get; internal set; } = FSItemType.FileSystemEntry;
        protected override void BeginProcessing()
{
            base.BeginProcessing();
            if (Depth == 0 && Recurse)
{
Depth = uint.MaxValue;
}
        }
        protected override void ProcessRecord() =>
WriteObject(
sendToPipeline: Path
.SelectMany(PathItem => Search(searchPath: PathItem, searchPattern: Filter, itemType: ItemType, currentDepth: 0, maxDepth: Depth)),
enumerateCollection: true
                );

private IEnumerable<string> Search(string searchPath, string searchPattern, FSItemType itemType, uint currentDepth, uint maxDepth)
=> GetFileSystemEntries(searchPath, searchPattern, itemType)
.Concat(
GetFileSystemEntries(searchPath, "*", FSItemType.Directory)
.Where(x => currentDepth + 1 <= maxDepth)
.SelectMany(directory => Search(directory, searchPattern, itemType, currentDepth + 1, maxDepth))
                    );
        private IEnumerable<string> GetFileSystemEntries(string path, string searchPattern, FSItemType itemType)
{
IEnumerable<string> fileSystemEntries = Array.Empty<string>();
//bool hasAccessError = false;
try
{
switch (itemType)
{
case FSItemType.FileSystemEntry:
fileSystemEntries = Directory.EnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly);
break;
case FSItemType.File:
fileSystemEntries = Directory.EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
                        break;
                    case FSItemType.Directory:
fileSystemEntries = Directory.EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
break;
}
}
catch (UnauthorizedAccessException ex)
{
WriteError(errorRecord: new ErrorRecord(
exception: ex,
errorId: "FSItemUnauthorizedAccessError",
errorCategory: ErrorCategory.PermissionDenied,
                  targetObject: path));
            }
catch (Exception ex)
{
WriteError(errorRecord: new ErrorRecord(
exception: ex,
errorId: "NotSpecified",
errorCategory: ErrorCategory.NotSpecified,
targetObject: path));
            }
            return fileSystemEntries;
        }
        public enum FSItemType
{
FileSystemEntry,
File,
Directory
}
}
}
 
  • Gefällt mir
Reaktionen: DPXone
Zurück
Oben