PowerShell – Fonctions, simples et avancées, aide intégrée5 minute(s) de lecture

En programmation, lorsque du code est plusieurs fois réutilisé, il est préférable pour gagner du temps d’écrire une fonction, qui permet d’effectuer une tâche précise avec de potentiels arguments, PowerShell n’échappe pas à la règle et propose la création de ce genre d’objet.

Fonction simple

Globalement une fonction est créé avec le mot réservé function il en est suivi du nom de la fonction et enfin entre accolade (et oui encore) les instructions à exécuter. Une fonction est appelée par son nom et ses différents paramètres. Ces derniers sont définis grâce à param(); pour créer un paramètre il faut simplement indiquer son nom comme une variable (si un type plus qu’un autre est attendu, comme pour les variables, il faut simplement ajouter entre crochet avant le type), s’ils sont plusieurs les séparer par une virgule (si une valeur par défaut est exigée si le paramètre n’est pas appelé, comme pour une variable après un = ajouter la valeur par défaut), les paramètres peuvent posséder des attributs (ces derniers seront détaillés dans la partie Fonction avancée) qui permettent de mieux générer les entrées. La variable $PSBoundParameters est instanciée à chaque fois qu’une fonction est appelée. C’est une variable locale de type Dictionnary. Les clés sont le nom des paramètres et la valeur associée la valeur donnée au paramètre. Une fonction peut avoir une valeur de retour, pour cela le mot return suivi ou non d’une variable (ou une valeur directement) peut être utilisé. Par exemple:

function Power {

	param (

		[Int32]$Number,
		[Int32]$Exponent = 2
	)

	$result [System.Math]::Pow($Number, $Exponent)
	return $result
}

Accessoirement pour demander un paramètre, une autre syntaxe existe:

function foo ($param) {}

Les paramètres sont appelés par un - précédant leur nom (ou leur alias) suivi d’une éventuelle valeur, ou alors en plaçant la valeur sur l’index de position du paramètre. Les variables définies à l’intérieur d’une fonction ne sont pas globales mais locales.

Fonction avancée

Ces fonctions sont vraiment plus intéressantes puisqu’elles imitent le comportement d’une cmdlet, les paramètres ont des attributs, et la fonction possède les parties BEGIN, PROCESS et END (non obligatoire) exactement comme en C#. De ce fait, des arguments “par défaut” lui sont attribués: -Verbose, -Debug etc. Ces paramètres permettent de modifier le comportement d’une fonction avancée tel une cmdlet, pour plus de renseignement vous pouvez regarder cet article de Microsoft. Une fonction avancée reprend la syntaxe d’une fonction simple, avec quelques modifications:

  • Le nom doit être composé d’un verbe et d’un nom, comme une cmdlet C#.
  • Avant la déclaration des paramètres, les différents attributs de fonctions, dont un est nécessaire pour avoir une fonction avancée [CmdletBinding()] qui peut prendre différents arguments (la documentation de Microsoft sur ce sujet), il en existe d’autre tel [OutputType()] qui prend en paramètre le type de sortie de la fonction (il est attendu le type littéral, comme celui précédant une variable typé; il peut y en avoir plusieurs chaque type étant séparé par une virgule).
  • Il est préférable pour les paramètres d’avoir des attributs.
  • Il est préférable que les instructions de la fonction soient dans des blocs tel BEGIN, PROCESS et END.

Commençons par les attributs de paramètre, ces derniers sont présents entre crochet sous forme d’accélérateur de type:

  • [Alias('NAME1', 'NAME2')] un attribut qui permet d’ajouter un alias non pas sur la cmdlet mais sur le nom du paramètre.
  • [ValidateCount(MIN,MAX)] un attribut qui permet de n’accepter que des listes dont la taille est comprise entre MIN et MAX.
  • [ValidateLenght(MIN,MAX)] un attribut qui permet de n’accepter qu’une chaîne de caractère dont la longueur est comprise entre MIN et MAX.
  • [ValidatePattern('REGEX')] un attribut qui permet de n’accepter qu’une chaîne de caractère qui valide une comparaison “regex” qui est définit par REGEX.
  • [ValidateRange(MIN, MAX)] un attribut qui permet de n’accepter qu’un entier situer entre MIN et MAX.
  • [ValidateScript(SCRIPT)] un attribut qui permet de n’accepter qu’un paramètre après sa vérification par un scriptblock SCRIPT, la valeur passé dans le paramètre est placé dans la variable $_.
  • [ValidateSet('VALUE1', 'VALUE2')] un attribut qui permet de n’accepter que si la valeur du paramètre est comprise dans le tableau @('VALUE1', 'VALUE2'), chaque valeur est séparé de la prochaine par une virgule.
  • [ValidateNotNull()] un attribut qui permet de ne pas accepter une valeur fourni si elle est égal à $null.
  • [ValidateDrive('DRIVE1', 'DRIVE2')] un attribut qui permet de ne pas accepter un chemin fourni si ce dernier n’est pas dans le tableau de PSDrive @('DRIVE1', 'DRIVE2').
  • [ValidateNotNullOrEmpty()] un attribut qui permet de ne pas accepter les paramètres dont la valeur est $null ou si la chaîne de caractère est vide.
  • [AllowNull()] permet d’accepter un paramètre dont la valeur est $null.
  • [AllowEmptyString()] permet d’accepter une chaîne de caractère vide.
  • [AllowEmptyCollection()] permet d’accepter des tableaux, liste par exemple dont la longueur est 0.
  • [switch] dont la valeur est un PSObject à une seul entrée .IsPresent qui est un booléen indiquant si l’argument a été appelé ou non.
  • [Parameter()] qui peut prendre en arguments plusieurs choses:
    • Mandatory un booléen qui détermine si le paramètre est nécessaire.
    • Position un entier qui définit la position par défaut du paramètre.
    • ValueFromPipeline un booléen qui définit si la variable peut prendre ses valeurs depuis un “pipeline”.
    • ValueFromPipelineByPropertyName est un booléen qui détermine si l’objet passé par la pipeline doit avoir le même nom de propriété que le nom du paramètre.
    • ParameterSetName une chaîne de caractères qui détermine un groupe de paramètre auquel ce dernier appartient.
    • helpMessage, est une chaîne de caractères qui apparaît lors de l’usage de Get-Help CMDLET -Parameter ParameterName.

La déclaration d’un paramètre exigeant des PSCredential est un peu particulière, il est en effet préférable de la faire ainsi (sans compter l’attribut Parameter()):

[Parameter(Mandatory=$false)]
[System.Management.Automation.CredentialAttribute()]
[System.Management.Automation.PSCredential]
$Credential = [System.Management.Automation.PSCredential]::Empty

Il en existe quelques autres que vous pouvez retrouver ici. PowerShell prend aussi en charge l’utilisation de paramètre dynamique, ils permettent la création ou non de paramètre lors de l’appel des paramètres d’une fonction avancée (ou une cmdlet). Plus globalement ces derniers sont créés dans une section nommée DynamicParameter suivie d’accolades dans lesquelles il y a les instructions. Si un paramètre est appelé il ne sera pas nul, donc la vérification de la présence peut se faire avec if. La création d’un paramètre reprend les étapes suivantes:

$paramDictionnary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

$attribute = New-Object System.Management.Automation.ParameterAttribute
$attribute.Mandatory = $true
$attribute.Position = 0
$attribute.ValueFromPipeline = $true
$attributeCollection.Add($attribute)

$attribute = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute
$attributeCollection.Add($attribute)

$attribute = New-Object System.Management.Automation.ValidateCountAttribute(1,2)
$attributeCollection.Add($attribute)

...

$Name = 'DynamicParameter'

$dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter($name, [String[]]; $attributeCollection)
$paramDictionnary.Add($Name, $dynParam)

$paramDictionnary

C’est plutôt complexe mais extrêmement pratique. La valeur des paramètres ne sera pas retournée directement, mais sera ajouté dans $PSBoundParameters.

Aide intégrée

Il est possible, comme en python par exemple, d’ajouter une aide intégrée à nos fonctions. Elle se définit au début de la fonction, entre commentaires multi lignes. Chaque segment permet d’ajouter une entrée à l’aide, on décompte:

  • .SYNOPSIS.
  • .DESCRIPTION.
  • .PARAMETER.
  • .EXAMPLE.
  • .NOTES.
  • .LINK.
  • .OUTPUTS.
  • .INPUTS.

Une fois un segment appelé, sa valeur doit être ajoutée sur la ligne de dessous, et durera jusqu’à la fin du commentaire. .PARAMETER prend un argument à sa droite le nom du paramètre. Certains segments sont définis lorsque certaines options sont ajoutées: helpMessage de l’attribut Parameter() permet de définir .PARAMETER et l’attribut [OutputType()] ajoute .OUTPUTS à l’aide. Il existe d’autres manières de définir l’aide d’une fonction, Laurent Dardenne (Microsoft PowerShell MVP, français qui plus ai) en propose une ici. Pour accéder à l’aide d’une fonction il faut utiliser la cmdlet Get-Help qui propose nombre de paramètres:

  • -Name précise le nom de la cmdlet.
  • -Full affiche toutes l’aide.
  • -Parameter suivit d’un paramètre, affiche l’aide d’un paramètre particulier.
  • -Examples affiche les exemples de la fonction.

Exemple d’aide intégrée:

<#
	.SYNOPSIS
		cette partie résume brièvement la fonction

	.DESCRIPTION
		cette partie explique plus en détail ce que fait la fonction

	.PARAMETER Parameter
		cette partie explique le rôle, les valeurs par défaut du paramètre Parameter
	
	.EXAMPLE
		Invoke-MyFunction -Parameter "hello"

	.NOTES
		cette partie exprime différentes remarques à l’égard de la fonction

	.LINK
		cette partie nous donne des éventuelles liens pour mieux comprendre le fonctionnement, ou les références à cette fonction

	.OUTPUTS
		cette partie informe des types de retour de la fonction

	.INPUTS
		cette partie informe des types d’entrée de la fonction
#>

Un petit projet de fin de dossier est disponible sur le blog, n’hésitez pas à aller le consulter, il met en pratique les différentes notions abordées durant ces derniers postes.

Leave a Reply