Pourquoi Powershell convertit-il silencieusement un tableau de chaînes avec un élément en chaîne

33

Considérez le script Powershell suivant, qui recherche les dossiers dans C: \ avec un «og» dans leur nom:

PS C: \> (ls |% {$ _. Nom} |? {$ _. Contient ("og")})
PerfLogs
Fichiers de programme
setup.log

Maintenant, je restreins la recherche pour obtenir un seul élément:

PS C: \> (ls |% {$ _. Nom} |? {$ _. Contient ("Prog")})
Fichiers de programme

Ce qui est étrange, c'est que la première opération produit un tableau , tandis que la deuxième opération (qui est à mon humble avis la même opération sémantique, donc elle devrait produire le même type de résultat) renvoie une chaîne . Cela peut être vu dans le résultat suivant:

PS C: \> (ls |% {$ _. Nom} |? {$ _. Contient ("og")}). Longueur
3
PS C: \> (ls |% {$ _. Nom} |? {$ _. Contient ("Prog")}). Longueur
13

Cela peut être très irritant, car apparemment il y a moins de dossiers qui correspondent à 'og' que ceux qui correspondent à 'Prog'.

Évidemment, PowerShell «décompresse» implicitement un tableau à un seul objet, et nous n'obtenons jamais un tableau de longueur 1. Il semble que chaque fois que je veux compter les résultats qui arrivent sur le pipeline, je dois vérifier si je » m traitant d'un tableau ou non.

Comment puis-je empêcher que cela se produise? Comment gérez-vous cela?

cheesus SO arrête de nuire à Monica
la source
Ceux-ci de StackOverflow peuvent aider: stackoverflow.com/questions/1827862/… stackoverflow.com/questions/1390782/… Si vous n'étiez pas en train de jouer $_.Contains, alors ça %{,,$_.Name}marche ...
Bob

Réponses:

56

De toute évidence, PowerShell «décompresse» implicitement un tableau à un seul élément vers un seul objet,

Et zéro élément se traduit par $null.

Comment puis-je empêcher que cela se produise?

Tu ne peux pas.

Comment gérez-vous cela?

Utilisez le constructeur de tableau ( @(...)) pour forcer une collection (éventuellement avec zéro ou un élément) return:

$res = @(ls | %{$_.Name} | ?{$_.Contains("Prog")})
Richard
la source
Merci, c'est parfait! Je voterai dès que j'aurai 15 points de réputation.
cheesus SO arrête de nuire à Monica
2
Pas sûr que vous puissiez le "forcer". @(1) | ConvertTo-Jsonretourne toujours 1au lieu de [1].
Marc
@Marc: ConvertTo-Jsonne renvoie jamais de collection: il lit la totalité de l'entrée et la convertit en une seule chaîne. Si vous voulez que les objets d'entrée soient convertis individuellement, vous devrez les traiter séparément.
Richard
1
@ Richard, je pense que vous vous méprenez: moi et bien d'autres, je veux essentiellement que l'objet entier (c'est-à-dire la collection) soit sérialisé (par exemple pour la persistance externe). Nous ne souhaitons pas traiter séparément chaque objet de la collection. ConvertTo-Json doit renvoyer une chaîne qui, si elle est exécutée, ConvertFrom-Json renvoie l'objet d'origine bien qu'un tableau / collection vide.
Marc
@Marc Le but de cette question est d'éviter le traitement d'un seul tableau d'éléments comme cet élément (ce qui est moins un problème en raison des modifications PSH ultérieures: notez la date de la question). Vous parlez d'un cas complètement différent (forçant une collection à être un seul objet) d'où mon malentendu.
Richard
2

Ce problème a été résolu dans PowerShell v3:

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

Sur une note latérale, vous pouvez trouver si un nom contient quelque chose à l'aide d'un caractère générique:

PS> ls *og*
Shay Levy
la source
6
Shay , je ne peux pas encore commenter les réponses, mais votre affirmation n'est pas vraie. PowerShell encadre toujours les éléments, mais ils ont, comme vous l'avez noté, donné aux éléments individuels une valeur "Count". Cependant, les résultats d'un seul élément ne sont toujours pas mis en boîte. Vous pouvez tester l'exemple ci-dessus contre PS 3 et voir les résultats.
Tohuw
1
Le comportement est toujours le même sur PS 5.
MEMark
Yep, def toujours présent
James Wiseman
1
Ce comportement est toujours le même dans PS 6.0.1
spuder
2

Notez la différence entre ces deux résultats:

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

Le fait est que le «déballage» est effectué par l'opération de canalisation. ConvertTo-Json voit toujours l'objet comme un tableau si nous utilisons InputObject plutôt que des tuyaux.

Larry Young
la source