25 Stimmen

Kombinieren Sie die Ergebnisse von zwei verschiedenen Get-ChildItem-Aufrufen in einer einzigen Variablen, um die gleiche Verarbeitung durchzuführen

Ich versuche, ein PowerShell-Skript zu schreiben, um eine Liste von Dateien aus mehreren Verzeichnissen zu erstellen. Nachdem alle Verzeichnisse zur Hauptliste hinzugefügt wurden, möchte ich die gleiche Verarbeitung für alle Dateien durchführen.

Das habe ich:

$items = New-Object Collections.Generic.List[IO.FileInfo]

$loc1 = @(Get-ChildItem -Path "\\server\C$\Program Files (x86)\Data1\" -Recurse)
$loc2 = @(Get-ChildItem -Path "\\server\C$\Web\DataStorage\" -Recurse)

$items.Add($loc1) # This line fails (the next also fails)
$items.Add($loc2)

# Processing code is here

die mit diesem Fehler fehlschlägt:

Das Argument "0" kann nicht konvertiert werden, mit Wert: "System.Object[]", für "Hinzufügen" in Typ "System.IO.FileInfo": "Kann nicht Konvertierung des Wertes "System.Object[]" vom Typ "System.Object[]" in den Typ "System.IO.FileInfo"."

Mich interessiert vor allem, was die richtige Vorgehensweise für diese Art von Situation ist. Mir ist klar, dass mein Code ein sehr C Wenn es eine bessere PowerShell-Methode gibt, um dieselbe Aufgabe zu erfüllen, bin ich dafür. Der Schlüssel ist, dass die Anzahl der $loc#'s können sich im Laufe der Zeit ändern, so dass das Hinzufügen und Entfernen von einem oder zwei im resultierenden Code einfach sein sollte.

38voto

Keith Hill Punkte 183005

Ich bin nicht sicher, ob Sie hier eine allgemeine Liste brauchen. Sie können einfach ein PowerShell-Array verwenden, z. B.:

$items  = @(Get-ChildItem '\\server\C$\Program Files (x86)\Data1\' -r)
$items += @(Get-ChildItem '\\server\C$\Web\DataStorage\' -r)

PowerShell-Arrays können verkettet werden mit += .

4 Stimmen

Vielleicht ist der Bedarf an @() sollte ausgeschlossen werden

0 Stimmen

@papo | Get-ChildItem does not output Array if the output contains only one item Trotzdem funktioniert es (zumindest auf meinem System, PS v5.1.14393.3471). Ich kann dies ohne Probleme tun: $Files = @(gci -Filter "$BuildName-$SemanticVersion*.nupkg"); $Files += @(gci -Filter "Setup.exe"); $Files .

0 Stimmen

@InteXX sorry habe nicht erwähnt, dass mein Kommentar eine Antwort auf Peters Kommentar war. Ihr Beispiel ist ähnlich wie das von Keith, also so wie es sein sollte, aber wenn Sie @() weglassen würden, wie Peter gefragt hat, könnte es sich falsch verhalten. Die Auto-Type-Funktion von PS ist nett, kann aber Probleme verursachen. Wenn Sie ein Array brauchen, seien Sie genau. Sonst könnte es zu einem Fehler kommen, oder in anderen Fällen zu einem Array of Array-Eintrag, der nicht erwartet wurde. Ich folge der Regel, wenn es ein Skript ist, sei sehr prägnant.

30voto

mjolinor Punkte 63365

Von get-help get-childitem: -Pfad Gibt einen Pfad zu einem oder mehreren Orten an. Wildcards sind erlaubt. Der Standardspeicherort ist das aktuelle Verzeichnis (.).

$items = get-childitem '\\server\C$\Program Files (x86)\Data1\','\\server\C$\Web\DataStorage\' -Recurse

8voto

Roman Kuzmin Punkte 38429

Hier ist eine vielleicht sogar PowerShell-ähnlichere Methode, die keine Teilverkettung oder explizites Hinzufügen von Elementen zum Ergebnis benötigt:

# Collect the results by two or more calls of Get-ChildItem
# and perhaps do some other job (but avoid unwanted output!)
$result = .{

    # Output items
    Get-ChildItem C:\TEMP\_100715_103408 -Recurse

    # Some other job
    $x = 1 + 1

    # Output some more items
    Get-ChildItem C:\TEMP\_100715_110341 -Recurse

    #...
}

# Process the result items
$result

Der Code innerhalb des Skriptblocks sollte jedoch etwas sorgfältiger geschrieben werden, um zu vermeiden, dass unerwünschte Ausgaben mit Elementen des Dateisystems vermischt werden.

EDIT: Alternativ, und vielleicht effektiver, anstelle von .{ ... } können wir verwenden. @( ... ) o $( ... ) wobei ... steht für den Code, der mehrere Aufrufe von Get-ChildItem .

5voto

Jaykul Punkte 14703

Keiths Antwort ist der PowerShell-Weg: Verwenden Sie einfach @(...)+@(...).

Wenn Sie tatsächlich eine typsichere Liste[IO.FileInfo] wollen, dann müssen Sie AddRange verwenden und das Objekt-Array in ein FileInfo-Array umwandeln. Sie müssen auch sicherstellen, dass Sie keine DirectoryInfo-Objekte erhalten, oder Sie müssen IO.FileSystemInfo als Listentyp verwenden:

Vermeiden Sie also Verzeichnisse:

$items = New-Object Collections.Generic.List[IO.FileInfo]
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Program Files (x86)\Data1\' -r | Where { -not $_.PSIsContainer } )) )
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Web\DataStorage\' -r | Where { -not $_.PSIsContainer } )) )

Oder verwenden Sie FileSystemInfo (die gemeinsame Basisklasse von FileInfo und DirectoryInfo):

$items = New-Object Collections.Generic.List[IO.FileSystemInfo]
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Program Files (x86)\Data1\' -r)) )
$items.AddRange( ([IO.FileSystemInfo[]](ls '\\server\C$\Web\DataStorage\' -r)) )

0 Stimmen

Dies ist die beste und präziseste Methode

0 Stimmen

Ich sollte hinzufügen, dass Sie jetzt (in PS4+) die -file einschalten Get-ChildItem (alias ls ) statt der Verwendung von | where-object { -not $_.PSIsContainer }

0 Stimmen

Ich meinte eigentlich Ihre/Keiths Lösung @(...) + @(...) :-)

1voto

KyleMit Punkte 34627

-Filter ist leistungsfähiger als -Include Wenn Sie also nicht viele verschiedene Erweiterungen haben, kann es schneller sein, einfach zwei gefilterte Listen miteinander zu verknüpfen.

$files  = Get-ChildItem -Path "H:\stash\" -Filter *.rdlc -Recurse 
$files += Get-ChildItem -Path "H:\stash\" -Filter *.rdl  -Recurse 

Ich habe die Ausgabe mit einem Timer wie diesem verglichen:

$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
# Do Stuff Here
$stopwatch.Stop()
Write-Host "$([Math]::Round($stopwatch.Elapsed.TotalSeconds)) seconds ellapsed"

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X