Dlaczego program PowerShell po cichu przekształca tablicę ciągów z jednym elementem w ciąg

33

Rozważ następujący skrypt Powershell, który wyszukuje foldery w C: \ z „og” w nazwie:

PS C: \> (ls |% {$ _. Nazwa} |? {$ _. Zawiera („og”)})
PerfLogs
Pliki programów
setup.log

Teraz zawężam wyszukiwanie, aby uzyskać tylko jeden przedmiot:

PS C: \> (ls |% {$ _. Nazwa} |? {$ _. Zawiera („Prog”)})
Pliki programów

Dziwne jest to, że pierwsza operacja daje tablicę , podczas gdy druga operacja (która jest semantycznie tą samą operacją IMHO, więc powinna dać ten sam typ wyniku) daje ciąg . Można to zobaczyć w następującym wyniku:

PS C: \> (ls |% {$ _. Nazwa} |? {$ _. Zawiera („og”)}). Długość
3)
PS C: \> (ls |% {$ _. Nazwa} |? {$ _. Zawiera („Prog”)}). Długość
13

Może to być bardzo irytujące, ponieważ najwyraźniej jest mniej folderów pasujących do „og” niż tych, które pasują do „Prog”.

Najwyraźniej PowerShell domyślnie „rozpakowuje” tablicę z jednym elementem do pojedynczego obiektu i nigdy nie otrzymujemy tablicy o długości 1. Wydaje się, że za każdym razem, gdy chcę policzyć wyniki nadchodzące przez potok, muszę sprawdzić, czy „ mam do czynienia z tablicą czy nie.

Jak mogę temu zapobiec? Jak sobie z tym radzisz?

cheesus więc przestań krzywdzić Monikę
źródło
Te z StackOverflow mogą pomóc: stackoverflow.com/questions/1827862/... stackoverflow.com/questions/1390782/... Jeśli nie korzystałeś z pipingu $_.Contains, to %{,,$_.Name}działa ...
Bob

Odpowiedzi:

56

Najwyraźniej PowerShell domyślnie „rozpakowuje” tablicę pojedynczych elementów do pojedynczego obiektu,

I zero wyników pozycji do $null.

Jak mogę temu zapobiec?

Nie możesz

Jak sobie z tym radzisz?

Użyj konstruktora tablic ( @(...)), aby wymusić zwrot kolekcji (być może z zerowym lub jednym elementem):

$res = @(ls | %{$_.Name} | ?{$_.Contains("Prog")})
Richard
źródło
Dziękuję, to jest idealne! Będę głosować, jak tylko będę mieć 15 punktów reputacji.
ser SO przestań krzywdzić Monikę
2
Nie jestem pewien, czy możesz to „wymusić”. @(1) | ConvertTo-Jsonnadal wraca 1zamiast [1].
Marc
@Marc: ConvertTo-Jsonnigdy nie zwraca kolekcji: odczytuje całe dane wejściowe i konwertuje na pojedynczy ciąg. Jeśli chcesz indywidualnie konwertować obiekty wejściowe, musisz przetworzyć każdy z nich osobno.
Richard
1
@Richard, myślę, że źle zrozumiałeś: ja i wiele innych, po prostu chcę, aby cały obiekt (tj. Kolekcja) został zserializowany (np. Dla zewnętrznego przetrwania). Nie jesteśmy zainteresowani przetwarzaniem każdego obiektu w kolekcji osobno. ConvertTo-Json powinien zwrócić ciąg znaków, który po uruchomieniu ConvertFrom-Json zwraca oryginalny obiekt, choć pustą tablicę / kolekcję.
Marc
@Marc Celem tego pytania jest uniknięcie traktowania tablicy pojedynczego elementu jako tego elementu (co jest mniejszym problemem ze względu na kolejne zmiany PSH: zwróć uwagę na datę pytania). Mówisz o zupełnie innym przypadku (zmuszając kolekcję do bycia jednym obiektem), stąd nieporozumienie.
Richard
2

Zostało to rozwiązane w PowerShell v3:

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

Na marginesie możesz sprawdzić, czy nazwa zawiera coś za pomocą symbolu wieloznacznego:

PS> ls *og*
Shay Levy
źródło
6
Shay , nie mogę jeszcze komentować odpowiedzi, ale twoje stwierdzenie nie jest prawdziwe. PowerShell nadal zawiera elementy, ale jak zauważyłeś, pojedynczym elementom przypisano wartość „Count”. Wyniki pojedynczego elementu są jednak nadal rozpakowane. Możesz przetestować powyższy przykład na PS 3 i zobaczyć wyniki.
Tohuw
1
Zachowanie jest nadal takie samo w PS 5.
UWAGI
Tak, def wciąż obecny
James Wiseman
1
To zachowanie jest nadal takie samo w PS 6.0.1
spuder
2

Zwróć uwagę na różnicę między tymi dwoma wynikami:

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

Chodzi o to, że „rozpakowywanie” odbywa się za pomocą operacji potoku. ConvertTo-Json nadal widzi obiekt jako tablicę, jeśli używamy InputObject zamiast potokowania.

Larry Young
źródło