Por que o Powershell converte silenciosamente uma matriz de strings com um item em uma string

33

Considere o seguinte script do Powershell, que procura pastas em C: \ com um 'og' no nome:

PS C: \> (ls |% {$ _. Nome} |? {$ _. Contém ("og")})
PerfLogs
Arquivos de Programas
setup.log

Agora refino a pesquisa para obter apenas um item:

PS C: \> (ls |% {$ _. Nome} |? {$ _. Contém ("Prog")})
Arquivos de Programas

O estranho é que a primeira operação gera uma matriz , enquanto a segunda operação (que é IMHO semanticamente a mesma operação, portanto deve produzir o mesmo tipo de resultado) gera uma string . Isso pode ser visto no seguinte resultado:

PS C: \> (ls |% {$ _. Nome} |? {$ _. Contém ("og")}). Comprimento
3
PS C: \> (ls |% {$ _. Nome} |? {$ _. Contém ("Prog")}). Comprimento
13

Isso pode ser muito irritante, pois aparentemente há menos pastas que correspondem a 'og' do que aquelas que correspondem a 'Prog'.

Evidentemente, o PowerShell implicitamente 'unboxes' uma matriz de item único para um único objeto, e nunca temos uma matriz de comprimento 1. Parece que toda vez que eu quero contar os resultados que chegam ao pipeline, tenho que verificar se eu ' estou lidando com uma matriz ou não.

Como posso impedir que isso aconteça? Como você lida com isso?

cheesus Então pare de prejudicar Monica
fonte
Estes partir StackOverflow pode ajudar: stackoverflow.com/questions/1827862/... stackoverflow.com/questions/1390782/... Se não foram tubulação para $_.Contains, em seguida, %{,,$_.Name}funciona ...
Bob

Respostas:

56

Evidentemente, o PowerShell implicitamente 'unboxes' uma matriz de item único para um único objeto,

E zero item resulta para $null.

Como posso impedir que isso aconteça?

Você não pode.

Como você lida com isso?

Use o construtor array ( @(...)) para forçar o retorno de uma coleção (possivelmente com zero ou um elemento):

$res = @(ls | %{$_.Name} | ?{$_.Contains("Prog")})
Richard
fonte
Obrigado, isso é perfeito! Vou votar assim que tiver 15 reputação.
cheesus ASSIM pare de prejudicar Monica
2
Não tenho certeza de que você pode "forçá-lo". @(1) | ConvertTo-Jsonainda retorna em 1vez de [1].
Marc
@ Marc: ConvertTo-Jsonnunca retorna uma coleção: lê toda a entrada e converte em uma única string. Se você deseja que os objetos de entrada sejam convertidos individualmente, será necessário processar cada um separadamente.
Richard
1
@ Richard, acho que você entendeu errado: eu e muitos outros basicamente queremos que todo o objeto (ou seja, coleção) seja serializado (por exemplo, para persistência externa). Não estamos interessados ​​em processar cada objeto na coleção separadamente. ConvertTo-Json deve retornar uma string que, se executada, ConvertFrom-Json retorna o objeto original, embora uma matriz / coleção vazia.
Marc
@ Marc O objetivo desta pergunta é evitar o tratamento de uma matriz de elemento único como esse elemento (que é menos problemático devido a alterações subsequentes no PSH: observe a data da pergunta). Você está falando de um caso completamente diferente (forçando uma coleção a ser um único objeto), portanto, me entendo mal.
Richard
2

Isso foi resolvido no PowerShell v3:

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

Em uma nota lateral, você pode descobrir se um nome contém algo usando um curinga:

PS> ls *og*
Shay Levy
fonte
6
Shay , ainda não posso comentar as respostas, mas sua afirmação não é verdadeira. O PowerShell ainda armazena elementos, mas eles, como você observou, atribuíram a itens únicos um valor "Contagem". Os resultados de item único ainda estão fora da caixa de seleção. Você pode testar o exemplo acima no PS 3 e ver os resultados.
Tohuw
1
O comportamento ainda é o mesmo no PS 5.
MEMark 4/16
Yep, def ainda apresentam
James Wiseman
1
Esse comportamento ainda é o mesmo no PS 6.0.1
spuder 05/04
2

Observe a diferença entre estes dois resultados:

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

O ponto é que o 'unboxing' está sendo feito pela operação do tubo. O ConvertTo-Json ainda vê o objeto como uma matriz se usarmos InputObject em vez de canalizar.

Larry Young
fonte