ForEach et Where magic methods
ForEach et Where sont deux concepts fréquemment utilisés qui sont disponibles dans PowerShell depuis la sortie de la version 1 en 2006. ForEach est disponible à la fois sous forme d’instruction et d’applet de commande (ForEach-Object), vous permettant de parcourir une collection d’objets et d’effectuer une action une fois pour chaque objet de cette collection. Where a été disponible en tant qu’applet de commande (Where-Object), vous permettant de filtrer les éléments d’une collection qui ne transmettent aucune condition, une condition qui peut être évaluée à l’aide des propriétés des objets de la collection ou des objets eux-mêmes appartenant à la collection. La capacité ForEach d’agir sur les éléments d’une collection et la capacité Where de filtrer une collection sont des fonctionnalités très utiles et incluses sous une forme ou une autre dans la logique derrière de nombreux scripts, commandes et modules PowerShell, quelle que soit la version de PowerShell utilisée. En fait, ils sont tellement utilisés qu’ils ont été un domaine d’intérêt pour l’amélioration des performances, des fonctionnalités et de la syntaxe dans les versions PowerShell 3.0 et 4.0.
Avec la sortie de Windows PowerShell 4.0, deux nouvelles méthodes « magiques » ont été introduites pour les types de collection qui fournissent une nouvelle syntaxe pour accéder aux capacités ForEach et Where dans Windows PowerShell. Ces méthodes sont bien nommées ForEach et Where. J’appelle ces méthodes « magiques » car elles sont assez magiques dans leur fonctionnement dans PowerShell. Ils n’apparaissent pas dans la sortie Get-Member, même si vous appliquez-Force et demandez-MemberType All. Si vous retroussez vos manches et creusez avec réflexion, vous pouvez les trouver; cependant, cela nécessite une recherche large car ce sont des méthodes d’extension privées implémentées sur une classe privée. Pourtant, même s’ils ne sont pas détectables sans jeter un coup d’œil sous les couvertures, ils sont là quand vous en avez besoin, ils sont plus rapides que leurs homologues plus âgés, et ils incluent des fonctionnalités qui n’étaient pas disponibles dans leurs homologues plus âgés, d’où le sentiment de « magie” qu’ils vous laissent lorsque vous les utilisez dans PowerShell. Malheureusement, ces méthodes restent non documentées même aujourd’hui, près d’un an après leur publication, tant de gens ne réalisent pas le pouvoir disponible dans ces méthodes. Cet article tentera de corriger cela en expliquant où ils peuvent être utilisés et comment ils fonctionnent afin que vous puissiez tirer parti de cette magie lorsque vous utilisez PowerShell.
Une note sur PowerShell 3.0
Avant d’expliquer comment fonctionnent les méthodes ForEach et Where, je dois mentionner quelque chose en ce qui concerne ces deux méthodes et PowerShell 3.0. S’il est vrai que les méthodes ForEach et Where n’étaient disponibles que dans PowerShell 4.0 et les versions ultérieures, PowerShell 3.0 est encore très largement utilisé dans de nombreux environnements, et à moins que vous n’utilisiez PowerShell dans un environnement standardisé sur PowerShell 4.0 et versions ultérieures, vous pouvez souhaiter profiter de la syntaxe fournie par les nouvelles méthodes lors de l’utilisation de PowerShell 3.0. J’ai senti que c’était une limitation qui valait la peine d’être abordée, donc dans le cadre du module TypePx que j’ai récemment publié sur GitHub et dans la Galerie de ressources PowerShell (aka le référentiel public PowerShellGet, actuellement en prévisualisation limitée), j’ai inclus des méthodes de script ForEach et Where qui sont fonctionnellement équivalentes aux méthodes introduites dans PowerShell 4.0 afin que vous puissiez exploiter la nouvelle syntaxe et fonctionnalité même si vous utilisez PowerShell 3.0. Il y a quelques lacunes dans cette mise en œuvre, que je soulignerai plus loin dans cet article.
La méthode ForEach
ForEach est une méthode qui vous permet de parcourir rapidement une collection d’objets et d’agir sur chaque objet de cette collection. Cette méthode offre des performances plus rapides que ses homologues plus anciens (l’instruction foreach et l’applet de commande ForEach-Object), et elle simplifie également certaines des actions les plus courantes que vous souhaitez effectuer sur les objets de la collection. Tous les objets sortis par cette méthode sont renvoyés dans une collection générique de type System.Collection.Modèle d’objet.Collection 1
.
Il existe six façons prises en charge d’invoquer cette méthode, et chacune d’entre elles sera expliquée plus en détail ci-dessous. Les arguments pris en charge qui peuvent être utilisés lors de l’appel de la méthode ForEach sont les suivants:
- ForEach(expression scriptblock)
- ForEach(type convertToType)
- ForEach(nom de propriété de chaîne)
- ForEach(nom de propriété de chaîne, valeur de l’objet)
- ForEach(nom de méthode de chaîne)
- ForEach(nom de méthode de chaîne, arguments d’objet)
- ForEach(expression de bloc de script, arguments d’objet)
Notez que ce sont des appariements d’arguments pris en charge, pas des surcharges différentes disponibles pour la méthode ForEach. L’utilisation d’appariements d’arguments autres que ceux-ci peut entraîner des erreurs qui n’identifient pas clairement le problème réel.
ForEach (expression scriptblock) et ForEach (expression scriptblock, arguments d’objet)
Si vous passez une expression de bloc de script dans la méthode ForEach, vous pouvez effectuer le même type de tâches que dans un bloc de script que vous utiliseriez avec l’instruction foreach ou l’applet de commande ForEach-Object. De plus, comme l’applet de commande ForEach-Object, les variables__ etPSPSItem font toutes deux référence à l’élément en cours de traitement. Tous les arguments que vous fournissez au-delà de l’argument de bloc de script initial seront utilisés comme arguments pour le bloc de script. C’est comme si le paramètre -ArgumentList fonctionnait sur l’applet de commande ForEach-Object. Voici un exemple démontrant comment vous pouvez l’utiliser pour exécuter un bloc de script sur chaque élément d’une collection:
# Get a set of services$services = Get-Service c*# Display the names and display names of all services in the collection$services.foreach{"$($_.Name) ($($_.DisplayName))"}# Select a property name to expand using a script block argument$services.foreach({param($PropertyName); $_.$PropertyName}, 'DisplayName')
Vous avez peut-être remarqué quelque chose d’étrange à propos de cette syntaxe, car je n’ai pas enveloppé le bloc de script lui-même entre parenthèses. Vous pouvez l’envelopper entre crochets ronds, mais cela est facultatif dans PowerShell 4.0 ou version ultérieure car l’analyseur PowerShell a été amélioré pour permettre l’omission des crochets chaque fois que vous appelez une méthode qui accepte un seul argument de bloc de script. De plus, comme l’instruction foreach et l’applet de commande ForEach-Object, le bloc de script que vous fournissez est appelé dans la portée actuelle. Cela signifie que toutes les affectations de variables que vous effectuez à l’intérieur de ce bloc de script persisteront une fois l’exécution de la méthode ForEach terminée.
ForEach(type convertToType)
Unique à la méthode ForEach, vous pouvez passer un type dans la méthode ForEach si vous souhaitez convertir chaque élément d’une collection en un autre type. Par exemple, imaginez que vous avez une collection d’objets et que vous souhaitez convertir ces objets en leur équivalent chaîne. Voici à quoi cela ressemblerait avec la méthode ForEach:
# Get a collection of processes$processes = Get-Process# Convert the objects in that collection into their string equivalent$processes.foreach()
Vous auriez pu effectuer la même tâche en tapant la collection dans un tableau de chaînes de type (par exemple]processesprocesses), et la typographie du tableau est en fait beaucoup plus rapide, mais il y a de très fortes chances que vous ne remarquiez même pas la différence de temps d’exécution à moins de travailler avec une très, très grande collection. Malgré le décalage horaire, j’aurai tendance à préférer la syntaxe de la méthode ForEach dans certaines situations si cela me permet de maintenir l’élégance dans l’implémentation en évitant les crochets ronds supplémentaires dans les scripts que j’écris.
ForEach(string PropertyName)
Dans PowerShell 3.0 et versions ultérieures, un deuxième ensemble de paramètres a été ajouté à ForEach-Object pour vous permettre de récupérer plus facilement la valeur d’une propriété spécifique en passant simplement un nom de propriété comme seule valeur de paramètre pour ForEach-Object. Cette commodité a également été offerte dans la méthode ForEach. Voici un exemple démontrant comment vous pouvez parcourir une collection et renvoyer une propriété de cette collection:
# Get all services whose name starts with "w"$services = Get-Service w*# Return the names of those services$services.foreach('Name')
Bien sûr, depuis la version 3.0 de PowerShell, vous pouvez simplement invoquer $services.Name pour obtenir les noms de tous les services, et cela se terminera plus rapidement que l’alternative à la méthode ForEach (bien que vous ne remarquerez que la différence de performance dans de très grandes collections de l’ordre de centaines de milliers d’objets); cependant, cela ne fonctionne que pour les propriétés qui ne sont pas sur la collection elle-même, et c’est une syntaxe avec laquelle certains scripteurs ne sont pas à l’aise en raison de la nature implicite de ce que fait la commande. La nouvelle syntaxe de la méthode ForEach vous offre une alternative plus explicite qui a l’avantage supplémentaire d’être un peu plus auto-documentée.
ForEach(string PropertyName, object NewValue)
Non seulement vous pouvez récupérer une propriété sur une collection d’objets, mais vous pouvez également définir une propriété sur une collection d’objets. Il s’agit d’une fonctionnalité qui n’est pas disponible dans les autres foreach, sauf si vous créez explicitement le bloc de script pour le faire. Pour définir la propriété, il vous suffit de fournir le nom de la propriété et la valeur que vous souhaitez utiliser lors de la définition de cette propriété, comme ceci :
# Note, this is not a realistic example# This would be used more commonly on configuration data$services = Get-Service c*# Now change the display names of every service to some new value$services.foreach('DisplayName','Hello')
Tout comme les affectations que vous feriez en utilisant l’opérateur equals, PowerShell tentera de convertir ce que vous fournissez comme nouvelle valeur dans le type approprié pour la propriété assignée.
ForEach(nom de méthode de chaîne) et ForEach(nom de méthode de chaîne, arguments d’objet)
Pour appeler une méthode, il vous suffit de fournir le nom de la méthode comme premier argument, puis les arguments de cette méthode comme deuxième, troisième, quatrième, etc. argument. Si la méthode ne prend aucun argument, vous pouvez simplement passer le nom de la méthode et elle sera invoquée sans aucun argument. Voici un exemple montrant comment vous pouvez tuer un tas de processus exécutant un programme spécifique:
# Get all processes running Chrome$processes = Get-Process -Name Chrome# Now kill all of those processes$processes.foreach('Kill')
Voici un autre exemple, cette fois en utilisant une méthode avec des arguments tout en montrant comment vous pouvez vérifier que les commandes suivent les meilleures pratiques en utilisant des noms et des alias appropriés pour les paramètres couramment utilisés:
# Get all commands that have a ComputerName parameter$cmds = Get-Command -ParameterName ComputerName# Now show a table making sure the parameter names and aliases are consistent$cmds.foreach('ResolveParameter','ComputerName') | Format-Table Name,Aliases
Comme vous pouvez le voir à partir de ces résultats, il y a certainement des incohérences dans l’implémentation des paramètres de nom d’ordinateur qui doivent être corrigées.
Qui couvre toutes les fonctionnalités actuellement disponibles dans la méthode ForEach. Comme vous pouvez le voir, il n’y a pas beaucoup de nouvelles fonctionnalités offertes dans cette méthode, mais les améliorations de syntaxe lorsque vous effectuez une tâche simple sur une collection d’objets sont agréables, et les améliorations des performances de la méthode ForEach par rapport à l’instruction foreach équivalente pour le pipeline ForEach-Object sont également une amélioration bienvenue. Avec cette explication à l’écart, passons à la méthode Where.
La méthode Where
Where est une méthode qui vous permet de filtrer une collection d’objets. Cela ressemble beaucoup à l’applet de commande Where-Object, mais la méthode Where ressemble également à Select-Object et Group-Object, inclut plusieurs fonctionnalités supplémentaires que l’applet de commande Where-Object ne prend pas en charge nativement par elle-même. Cette méthode offre des performances plus rapides que Where-Object dans une commande simple et élégante. Comme la méthode ForEach, tous les objets sortis par cette méthode sont renvoyés dans une collection générique de type System.Collection.Modèle d’objet.Collection1.
Il n’existe qu’une seule version de cette méthode, qui peut être décrite comme suit:
Where(scriptblock expression])
Comme indiqué par les crochets, le bloc de script d’expression est requis et l’énumération de mode et l’argument entier numberToReturn sont facultatifs, vous pouvez donc invoquer cette méthode en utilisant 1, 2 ou 3 arguments. Si vous souhaitez utiliser un argument particulier, vous devez fournir tous les arguments à gauche de cet argument (c’est-à-dire que si vous souhaitez fournir une valeur pour numberToReturn, vous devez également fournir des valeurs pour le mode et l’expression).
Where(expression scriptblock)
L’invocation la plus basique de la méthode Where prend simplement une expression de bloc de script comme argument. L’expression de bloc de script sera évaluée une fois pour chaque objet de la collection en cours de traitement, et si elle renvoie true, l’objet sera renvoyé par la méthode Where. C’est l’équivalent fonctionnel de l’appel de l’applet de commande Where-Object et de lui passer un bloc de script. Comme l’applet de commande Where-Object, les variables Where_ etPSPSItem peuvent être utilisées pour faire référence à l’élément en cours de traitement à l’intérieur du bloc de script.
Voici un exemple très simple, montrant comment vous pouvez obtenir une liste de services en cours d’exécution.
# Get all services$services = Get-Service# Now filter out any services that are not running$services.where{$_.Status -eq 'Running'}
Cela n’offre aucune nouvelle fonctionnalité, mais il offre des performances beaucoup plus rapides que Where-Object et la syntaxe est assez facile à suivre, vous devriez donc vraiment en tenir compte pour vos scripts lorsque vous effectuez un filtrage côté client des collections que vous avez stockées dans une variable.
Where (expression scriptblock, mode WhereOperatorSelectionMode)
Lorsque vous commencez à examiner les paramètres facultatifs de la méthode Where, les choses commencent à devenir beaucoup plus intéressantes. La version 4.0 de Windows PowerShell comprenait une nouvelle énumération avec un nom de type de système.Gestion.Automatisation.Oùoperatorselectionmode. Notez le suffixe de ce nom de type: « SelectionMode ». Il est utilisé pour fournir de puissantes capacités de sélection dans une syntaxe Where. Voici les valeurs incluses dans cette énumération, ainsi que leurs définitions:
Par défaut | Filtrez la collection à l’aide du bloc de script d’expression, jusqu’à un nombre maximum s’il en existe un ou par défaut pour tous les objets du bloc de script d’expression collecte si aucun nombre maximal n’a été fourni dans numberToReturn. |
First | Renvoie les N premiers objets qui passent le filtre de bloc de script d’expression, par défaut à seulement 1 objet si un nombre spécifique n’a pas été demandé dans numberToReturn. |
Last | Renvoie les N derniers objets qui passent le filtre de bloc de script d’expression, par défaut à seulement 1 objet si un nombre spécifique n’a pas été demandé dans numberToReturn. |
SkipUntil | Ignore les objets de la collection jusqu’à ce qu’un objet passe le filtre de bloc de script d’expression, puis renvoie les N premiers objets, par défaut à tous les objets restants si aucun nombre maximum n’a été fourni dans numberToReturn. |
Jusqu’à ce que | Renvoie les N premiers objets d’une collection jusqu’à ce qu’un objet passe le filtre de bloc de script d’expression, par défaut pour tous les objets menant au premier objet qui est passé si aucun nombre maximum n’a été fourni dans numberToReturn. |
Split | Scindez une collection en deux, en plaçant tous les objets qui passent le filtre de bloc de script d’expression dans la première collection jusqu’à un nombre maximum s’il en existe un dans numberToReturn, ou tous les objets qui passent si aucun nombre maximum n’a été fourni, et en plaçant tous les autres objets qui ne sont pas placés dans la première collection dans la deuxième collection. |
Chacun de ces éléments offre une valeur unique lorsque vous traitez des collections de données, je vais donc fournir plus de détails sur chaque mode de sélection ci-dessous.
Default
Sans surprise, la valeur par défaut de l’argument mode est ‘Default’. Le mode de sélection par défaut offre les mêmes fonctionnalités que lorsque vous ne fournissez aucun mode de sélection. Par exemple, nous aurions pu écrire la dernière ligne de notre exemple précédent comme ceci:
# Now filter out any services that are not running$services.where({$_.Status -eq 'Running'},'Default')
Dans cet exemple, l’argument supplémentaire n’est cependant pas nécessaire, car il fait exactement la même chose que si vous ne fournissez pas l’argument. Vous pouvez également fournir le nombre maximum d’objets que vous souhaitez renvoyer lors de l’utilisation du mode de sélection par défaut à l’aide de l’argument numberToReturn, comme ceci:
# Get the first 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'Default',10)
Il est important de noter que la fonctionnalité exacte est également disponible lors de l’utilisation du Premier mode de sélection (dont nous parlerons dans un instant), il n’est donc vraiment pas pratique d’utiliser l’un des paramètres optionnels lorsque vous utilisez le mode de sélection par défaut.
First
Comme vous l’avez peut-être deviné, le Premier mode de sélection vous permet de sélectionner le ou les premiers objets de la collection qui passent le filtre d’expression de bloc de script. Lorsque vous utilisez d’abord sans valeur pour l’argument numberToReturn, ou lorsque vous utilisez d’abord avec une valeur de 0 pour l’argument numberToReturn, seul le premier objet qui passe le filtre sera renvoyé. Vous pouvez éventuellement spécifier le nombre d’objets à renvoyer dans l’argument numberToReturn, auquel cas de nombreux objets seront renvoyés (en supposant qu’il y ait autant d’objets qui passent le filtre).
Voici quelques exemples d’utilisation de notre collection de services montrant le premier mode de sélection en action:
# Get the first service in our collection that is running$services.where({$_.Status -eq 'Running'},'First')# Get the first service in our collection that is running$services.where({$_.Status -eq 'Running'},'First',1)# Get the first 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'First',10)
Notez que la deuxième commande dans ces exemples renvoie les mêmes résultats que la première commande car elle transmet simplement explicitement la valeur par défaut de l’argument numberToReturn lorsque le Premier mode de sélection est utilisé.
Dernier
Le Dernier mode de sélection fonctionne un peu comme le Premier mode de sélection, vous permettant de sélectionner le ou les derniers objets de la collection qui passent le filtre d’expression de bloc de script. Lorsque vous utilisez Last sans valeur pour l’argument numberToReturn, ou lorsque vous utilisez Last avec une valeur de 0 pour l’argument numberToReturn, seul le dernier objet qui passe le filtre sera renvoyé. Vous pouvez éventuellement spécifier le nombre d’objets à renvoyer dans l’argument numberToReturn, auquel cas de nombreux objets seront renvoyés (en supposant qu’il y ait autant d’objets qui passent le filtre).
Voici quelques exemples d’utilisation de notre collection de services montrant le dernier mode de sélection en action:
# Get the last service in our collection that is running$services.where({$_.Status -eq 'Running'},'Last')# Get the last service in our collection that is running$services.where({$_.Status -eq 'Running'},'Last',1)# Get the last 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'Last',10)
Comme les Premiers exemples de mode de sélection, la deuxième commande dans ces exemples renvoie les mêmes résultats que la première commande car elle passe simplement explicitement la valeur par défaut de l’argument numberToReturn lorsque le Dernier mode de sélection est utilisé.
SkipUntil
Le mode de sélection SkipUntil vous permet d’ignorer tous les objets d’une collection jusqu’à ce que vous en trouviez un qui passe le filtre d’expression de bloc de script. Une fois que vous avez trouvé un objet qui passe le filtre, le mode SkipUntil retournera tous les objets restants dans la collection si aucune valeur ou une valeur de 0 n’a été fournie à l’argument numberToReturn, ou il retournera les N premiers objets restants dans la collection si une valeur supérieure à zéro a été fournie à l’argument numberToReturn. Dans les deux cas, les résultats incluront le premier objet qui a passé le filtre.
Voici quelques exemples d’utilisation d’un sous-ensemble de notre collection de services pour afficher le mode de sélection SkipUntil en action:
# Get a collection of services whose name starts with "c"$services = Get-Service c*# Skip all services until we find one with a status of "Running"$services.where({$_.Status -eq 'Running'},'SkipUntil')# Skip all services until we find one with a status of "Running", then# return the first 2$services.where({$_.Status -eq 'Running'},'SkipUntil',2)
Until
Le mode de sélection Until fournit la fonctionnalité opposée du mode de sélection SkipUntil. Il vous permet de renvoyer des objets dans une collection jusqu’à ce que vous en trouviez un qui passe le filtre d’expression de bloc de script. Une fois que vous avez trouvé un objet qui passe le filtre, la méthode Where arrête le traitement des objets. Si vous ne fournissez pas de valeur pour l’argument numberToReturn, ou si vous fournissez une valeur de 0, le mode de sélection Until renverra tous les objets de la collection menant au premier qui passe le filtre d’expression de bloc de script. Si vous fournissez une valeur pour l’argument numberToReturn supérieure à 0, le mode de sélection Until renverra au maximum ce nombre d’objets, ce qui signifie qu’il peut même ne pas trouver un objet qui passe le filtre d’expression de bloc de script.
Voici quelques exemples utilisant un sous-ensemble différent de notre collection de services pour afficher le mode de sélection Until en action :
# Get a collection of services whose name starts with "p"$services = Get-Service p*# Return all services until we find one with a status of "Stopped"$services.where({$_.Status -eq 'Stopped'},'Until')# Return the first 2 services unless we find one with a status of# "Stopped" first$services.where({$_.Status -eq 'Stopped'},'Until',2)
Split
Le mode de sélection Split est unique. Au lieu de renvoyer un sous-ensemble de la collection avec laquelle vous commencez dans une nouvelle collection, il renvoie une nouvelle collection qui contient en interne deux collections distinctes. Ce que contiennent ces collections imbriquées dépend de la façon dont vous utilisez le mode de sélection fractionnée. Split vous permet de diviser une collection d’objets en deux. Par défaut, si vous ne fournissez pas de valeur pour l’argument numberToReturn ou si vous fournissez une valeur de 0 pour l’argument numberToReturn, Split placera tous les objets qui transmettent le filtre d’expression de bloc de script dans la première collection imbriquée, et tous les autres objets (ceux qui ne transmettent pas le filtre d’expression de bloc de script) dans la deuxième collection imbriquée. Si vous fournissez une valeur supérieure à 0 pour l’argument numberToReturn, split limitera la taille de la première collection à ce montant maximum, et tous les objets restants de la collection, même ceux qui correspondent au filtre d’expression de bloc de script, seront placés dans la deuxième collection.
Voici quelques exemples montrant comment le mode de sélection fractionnée peut être utilisé pour diviser une collection d’objets de différentes manières:
# Get all services$services = Get-Service# Split the services into two groups: Running and not Running$running,$notRunning = $services.Where({$_.Status -eq 'Running'},'Split')# Show the Running services$running# Show the services that are not Running$notRunning# Split the services into the same two groups, but limit the Running group# to a maximum of 10 items$10running,$others = $services.Where({$_.Status -eq 'Running'},'Split',10)# Show the first 10 Running services$10running# Show all other services$others
Comme vous pouvez le voir dans cet exemple, Split est un mode de sélection assez puissant, offrant un mélange de filtrage, de regroupement et de sélection en un seul appel de commande.
Cette collection de modes de sélection rend la méthode Where rapide et élégante, mais en même temps plus puissante que les Where-Object, Group-Object et Select-Object combinés dans un seul pipeline. Qu’y a-t-il à ne pas aimer à ce sujet?
Lacunes dans le ForEach et Où les méthodes de script dans TypePx
Comme je l’ai mentionné plus tôt dans cet article, j’ai écrit des extensions de type pour PowerShell 3.0 et plus tard et les a emballés dans un module appelé TypePx. TypePx est un module de script entièrement écrit en PowerShell, et il fonctionne sur PowerShell 3.0 ou version ultérieure. Si vous utilisez PowerShell 3.0 (et seulement si vous utilisez PowerShell 3.0), TypePx définit les méthodes de script ForEach et Where qui imitent le comportement de ForEach et les méthodes Where dans PowerShell 4.0 et versions ultérieures. Alors que le système de type étendu dans PowerShell permet d’imiter ce comportement, il y a quelques lacunes dues à l’implémentation dans PowerShell qui ont affecté jusqu’où j’ai pu aller avec ces extensions de type. Cette section décrira certaines des différences et limitations qui existent dans les méthodes de script ForEach et Where dans TypePx que vous voudrez peut-être connaître si vous utilisez PowerShell 3.0.
Blocs de script invoqués à partir d’une méthode de script exécutée dans une portée enfant
Contrairement à ForEach et où les méthodes implémentées dans PowerShell 4.0 ou une version ultérieure qui appelle le bloc de script d’expression dans la portée actuelle où la méthode est appelée, ForEach et Où les méthodes de script implémentées dans PowerShell 3.0 appellent le bloc de script d’expression dans une portée enfant. C’est une limitation dans PowerShell qui existe depuis le tout début (une limitation, je pourrais ajouter, qui je pense est de loin la plus grande lacune de PowerShell en tant que langage).
En raison de cette limitation, toutes les variables que vous affectez à l’intérieur d’un bloc de script d’expression ne seront modifiées que dans la portée enfant. Cela a des implications si votre bloc de script d’expression est destiné à mettre à jour une variable dans la portée dans laquelle vous appelez ForEach ou Where. Il est peu probable que cela pose un problème lors de l’utilisation de Where car il n’est pas très courant de modifier des variables dans un bloc de script d’expression Where, mais dans les blocs de script ForEach, cela peut poser un problème, vous devez donc garder cela à l’esprit si vous utilisez ces extensions.
Je dois noter que je voudrais supprimer complètement cette limitation, et je crois que je pourrai le faire, mais au moment où j’ai écrit cet article, je n’ai pas encore implémenté de correctif pour cela.
La plupart des collections, mais pas toutes, auront des méthodes ForEach et Where
Dans PowerShell 4.0 et versions ultérieures, les méthodes ForEach et Where sont mises à disposition par magie pour tous les types qui implémentent IEnumerable sauf pour String, XmlNode et les types qui implémentent IDictionary. Dans PowerShell, le système de type étendu ne permet pas la création d’extensions pour les interfaces, uniquement pour les types. C’est un défi si vous souhaitez créer une large extension comme ForEach et Where. Dans l’implémentation actuelle de ces extensions dans TypePx, le module TypePx trouve tous les types dans tous les assemblys chargés dans le domaine d’application actuel, et pour tous les types non génériques qui définissent IEnumerable mais pas IDictionary (à l’exclusion de String et XmlNode), plus pour tous les types génériques qui définissent IEnumerable mais pas IDictionary pour les collections génériques de PSObject, Object, String, Int32 ou Int64, les méthodes ForEach et Where script seront créées.
Cela couvre un grand nombre de types qui, dans mes propres tests, ont été suffisants, mais vous pouvez rencontrer des types où vous souhaitez utiliser ces méthodes et ils ne sont pas disponibles. Si c’est le cas, faites-le moi savoir via GitHub et je verrai ce que je peux faire. C’est également une limitation que je voudrais supprimer, mais j’ai besoin de plus de temps pour rechercher comment implémenter la fonctionnalité équivalente dans un assembly compilé où je pourrai peut-être la définir plus comme elle est définie dans PowerShell 4.0 et versions ultérieures.
Les scripts PowerShell ne sont pas aussi rapides que le code compilé
Cela va probablement de soi, mais lorsque vous écrivez quelque chose dans PowerShell, qui est un langage interprété, il ne s’exécutera pas aussi rapidement que si vous écriviez la logique équivalente dans un langage comme C #. C’est absolument le cas pour les méthodes de script ForEach et Where, qui utilisent en interne ForEach-Object, Where-Object et pipelining pour imiter le comportement des méthodes ForEach et Where natives. Dans ce cas, l’avantage d’avoir ces commandes vient de la syntaxe et des fonctionnalités élégantes qu’elles fournissent, ainsi que de pouvoir les utiliser dans des scripts pour PowerShell 3.0 et 4.0. Les avantages en termes de performances dans ForEach et Where ne sont que dans l’implémentation native de PowerShell 4.0.
PowerShell 3.0 nécessite des crochets autour de tous les paramètres de méthode
J’ai mentionné dans les exemples ci-dessus que j’ai pu invoquer une méthode avec un seul paramètre de bloc de script littéral sans envelopper ce bloc de script littéral dans des crochets supplémentaires. Cette capacité n’existe que dans PowerShell 4.0 ou plus tard en raison des améliorations apportées à l’analyseur dans cette version. Dans PowerShell 3.0, l’analyseur ne prend pas en charge cela, donc les crochets sont toujours nécessaires pour que les méthodes ForEach et Where script fonctionnent avec un seul paramètre de bloc de script littéral dans cette version.
Conclusion
Quand j’ai commencé à essayer d’imiter le comportement de ForEach et Où les méthodes magiques dans PowerShell 3.0, je ne réalisais pas vraiment à quel point elles fournissaient des fonctionnalités. Creuser dans les détails techniques de ces méthodes afin de pouvoir créer les extensions que je voulais dans TypePx a permis de découvrir toutes les fonctionnalités cachées de ces méthodes très puissantes, et je suis très heureux de les partager avec vous dans cet article. J’espère que ces informations vous aideront à tirer parti de ce merveilleux nouvel ensemble de fonctionnalités dans votre travail PowerShell, même si vous utilisez toujours PowerShell 3.0. Bonne scénarisation !
Leave a Reply