Warum konvertiert Powershell ein String-Array mit einem Element stillschweigend in einen String?

33

Betrachten Sie das folgende Powershell-Skript, das nach Ordnern in C: \ mit einem 'og' im Namen sucht:

PS C: \> (ls |% {$ _. Name} |? {$ _. Enthält ("og")})
PerfLogs
Programmdateien
setup.log

Jetzt grenze ich die Suche ein, um nur einen Artikel zu erhalten:

PS C: \> (ls |% {$ _. Name} |? {$ _. Enthält ("Prog")})
Programmdateien

Das Seltsame ist, dass die erste Operation ein Array ergibt , während die zweite Operation (die meiner Meinung nach semantisch dieselbe Operation ist, also dieselbe Art von Ergebnis liefern sollte) eine Zeichenfolge ergibt . Dies ist im folgenden Ergebnis zu sehen:

PS C: \> (ls |% {$ _. Name} |? {$ _. Enthält ("og")}). Länge
3
PS C: \> (ls |% {$ _. Name} |? {$ _. Enthält ("Prog")}). Länge
13

Dies kann sehr irritierend sein, da es anscheinend weniger Ordner gibt, die mit 'og' übereinstimmen als mit 'Prog'.

Offensichtlich 'entpackt' PowerShell implizit ein einzelnes Elementarray für ein einzelnes Objekt, und es wird nie ein Array der Länge 1 angezeigt. Es scheint, dass ich jedes Mal, wenn ich die über die Pipeline kommenden Ergebnisse zählen möchte, überprüfen muss, ob ich Ich habe es mit einem Array zu tun oder nicht.

Wie kann ich das verhindern? Wie gehst du damit um?

Käse SO hör auf, Monica Schaden zuzufügen
quelle
Diese von StackOverflow können helfen: stackoverflow.com/questions/1827862/… stackoverflow.com/questions/1390782/… Wenn Sie nicht weitergeleitet wurden $_.Contains, dann %{,,$_.Name}funktioniert ...
Bob

Antworten:

56

Offensichtlich 'entpackt' PowerShell implizit ein einzelnes Elementarray in ein einzelnes Objekt.

Und null Artikel ergibt sich zu $null.

Wie kann ich das verhindern?

Das kannst du nicht.

Wie gehst du damit um?

Verwenden Sie den Array-Konstruktor ( @(...)), um eine Auflistung (möglicherweise mit null oder einem Element) zu erzwingen, die Folgendes zurückgibt:

$res = @(ls | %{$_.Name} | ?{$_.Contains("Prog")})
Richard
quelle
Danke, das ist perfekt! Ich stimme zu, sobald ich 15 Ruf habe.
cheesus SO hör auf, Monica am
2
Ich bin nicht sicher, ob du es "erzwingen" kannst. @(1) | ConvertTo-Jsonkehrt immer noch 1statt [1].
Marc
@Marc: Gibt ConvertTo-Jsonniemals eine Sammlung zurück. Es liest die gesamte Eingabe und konvertiert sie in eine einzelne Zeichenfolge. Wenn Sie Eingabeobjekte einzeln konvertieren möchten, müssen Sie sie einzeln verarbeiten.
Richard
1
@Richard, ich glaube, Sie haben ein Missverständnis: Ich und viele andere möchten grundsätzlich, dass das gesamte Objekt (dh die Sammlung) serialisiert wird (z. B. aus Gründen der externen Beständigkeit). Wir sind nicht daran interessiert, jedes Objekt in der Sammlung einzeln zu bearbeiten. ConvertTo-Json sollte einen String zurückgeben, durch den ConvertFrom-Json das ursprüngliche Objekt zurückgibt, obwohl es sich um ein leeres Array / eine leere Auflistung handelt.
Marc
@Marc Bei dieser Frage geht es darum, die Behandlung eines einzelnen Elementarrays als dieses Element zu vermeiden (was aufgrund späterer PSH-Änderungen weniger problematisch ist: Notieren Sie sich das Datum der Frage.) Sie sprechen von einem völlig anderen Fall (eine Sammlung zu zwingen, ein einzelnes Objekt zu sein), daher mein Missverständnis.
Richard
2

Dies wurde in PowerShell v3 behoben:

http://blogs.microsoft.co.il/blogs/scriptfanatic/archive/2012/03/19/Counting-objects-in-PowerShell-3.0.aspx

Nebenbei können Sie mithilfe eines Platzhalters feststellen, ob ein Name etwas enthält:

PS> ls *og*
Shay Levy
quelle
6
Shay , ich kann noch keine Kommentare zu Antworten abgeben, aber deine Aussage ist nicht wahr. PowerShell fasst weiterhin Elemente, aber sie haben, wie Sie bemerkt haben, einzelnen Elementen einen "Count" -Wert gegeben. Einzelne Artikelergebnisse sind jedoch immer noch nicht gepackt. Sie können das obige Beispiel gegen PS 3 testen und die Ergebnisse sehen.
Tohuw
1
Das Verhalten ist in PS 5 immer noch dasselbe.
MEMark
Ja, auf jeden Fall noch anwesend
James Wiseman
1
Dieses Verhalten ist immer noch dasselbe in PS 6.0.1
Spuder
2

Beachten Sie den Unterschied zwischen diesen beiden Ergebnissen:

PS C:\> ConvertTo-Json -InputObject @(1)
[
    1
]
PS C:\> @(1)|ConvertTo-Json
1
PS C:\>

Der Punkt ist, dass das 'Unboxing' durch die Pipe-Operation erledigt wird. ConvertTo-Json sieht das Objekt weiterhin als Array, wenn InputObject anstelle von Piping verwendet wird.

Larry Young
quelle