Question [Astuce]Affichage de debug de segments de pipeline

Plus d'informations
il y a 15 ans 5 mois #3141 par Laurent Dardenne
On souhaite parfois afficher le nombre d'éléments reçus dans un segment de pipeline, le code suivant permet cette opération :
[code:1]
1,2|`
Foreach {$I=0} {$I++;$_} {if ($I -eq 0)
{Write-Debug \"Segment 1 : aucun éléments.\"}
else {Write-Debug \"Segment 1 : $I éléments trouvés.\"}
}
#Segments suivants...
[/code:1]
Il reste possible d'utiliser un scriptblock déclarant uniquement le bloc End, dans ce cas la variable $Input contient la totalité des objets émis par le segment précédent.
par exemple :
[code:1]
1,2|&{ End {$I=($Input|% {$_}).count;if ($i -eq $null) {$I=0}; Write-Debug \" $I éléments trouvés.\"} }
[/code:1]
Dans tous ces cas de debug on souhaite modifier rapidement les instructions du pipeline afin de visualiser les résultats intermédiaires.
Cela permet par exemple de visualiser les améliorations lors d'optimisation. Bien évidemment moins on traite d'objets plus rapide est le traitement.

Pour réduire la saisie au strict minimum on peut créer une fonction s'appuyant sur le dynamisme de PowerShell.
L'objectif étant d'arriver au code suivant :
[code:1]
1,2|&$(ViewSegment \"Test\"«»)
[/code:1]
On appel une fonction qui crée un scriptbloc que l'on exécute dans le segment du pipeline, le résultat :
[code:1]
1
2
DEBUG: Segment Test (1) : 2 éléments trouvés.
[/code:1]
Voici une solution détaillée :
[code:1]
function Create-ViewSegmentSB([String] $VariableName=$(throw \"Le nom de la variable doit être précisé.\") )
{ #Crée un scriptbloc, déclarant les trois blocs de code Begin, Process et End, utilisé
# à des fins de debug ($DebugPreference=\"Continue\")
#
# L'instruction suivante affiche le nombre d'éléments reçus du segment précédent.
# 1,2|Foreach {$I=0} {$I++;$_} {if ($I -eq 0) {Write-Debug \"Segment 1 : aucun éléments.\"}
# else {Write-Debug \"Segment 1 : $I éléments trouvés.\"}}
# 1
# 2
# DEBUG: Segment 1 : 2 éléments trouvés.

#Il reste possible, en lieu et place de cette fonction, d'utiliser un scriptblock déclarant uniquement
#le bloc End, dans ce casla variable $input contient la totalité des objets émis par le segment précédent.
# par exemple :
#
# 1,2|&{ End {$I=($input|% {$_}).count;if ($i -eq $null) {$I=0}; Write-Debug \" $I éléments trouvés.\"} }
# 1
# 2
# DEBUG: 2 éléments trouvés.
#
# $sb={ End {$I=($input|% {$_}).count;if ($i -eq $null) {$I=0}; Write-Debug \" $I éléments trouvés.\"} }
# @()|&$sb
# DEBUG: 0 éléments trouvés.


#Tests de validité du nom de variable
if ($VariableName -eq [String]::Empty)
{throw \"Le nom de la variable locale n'est pas renseigné.\"}

if ($VariableName.Contains('$') )
{throw \"L'expansion de variable n'est pas supportée.\"}

#Stop le pipeline
trap {break }
&{ #Gére l'erreur du scriptblock de validation du nom de variable
trap { Throw \"Le nom de variable ( $VariableName ) pose problème.\" }
#Test la validité du nom de variable
# Si VariableName=\"Test\" alors on exécute l'instruction $Test=0 et ce sans erreur
# Si VariableName=\"Te/st\" alors on exécute l'instruction $Te/st=0 ce qui provoque
# une erreur de syntaxe dans le scriptbloc, cette erreur est propagée vers l'appelant.
#Au lieu de tester les différentes possibilités on génère un code de test
# puis on l'exécute.
&$($ExecutionContext.InvokeCommand.NewScriptBlock(\"`$$VariableName=0\"))
}

#On génère le code du scriptbloc à l'aide de l'expansion de variable,
# puis on crée un scriptbloc qui est renvoyé dans le pipeline.
#
#L'appel du scriptblock généré se fera ainsi :
# 1,2|&$(Create-ViewSegmentSB \"I\" )
# ou
# $D=Create-ViewSegmentSB \"PremierSegment\"
# 1,2|&$D \"H\"
# ou encore
# New-Alias ViewSegment Create-ViewSegmentSB
# 1,2|&$(ViewSegment \"I\")
# New-Alias vwsg Create-ViewSegmentSB
# 1,2|&$(vwsg \"I\")
# renvoi :
# 1
# 2
# DEBUG: Segment I (1) : 5 éléments trouvés.
#
#Où I est le nom de la variable (le \"nom\" du segment)
# et (1) la position du segment courant
$ExecutionContext.InvokeCommand.NewScriptBlock(@\"
#Initialise la variable
# Si $VariableName=\"I\", pour {`$$VariableName=0} on génére {$I=0}
Begin {`$$VariableName=0}
#incrémente le compteur puis réémet l'objet
Process {`$$VariableName++;`$_}
#Affichage final
End {if (`$$VariableName -eq 0)
# $MyInvocation.PipeLinePosition permet de récupèrer la position du segment courant
# dans le pipeline
{ Write-Debug `\"Segment $VariableName (`$(`$MyInvocation.PipeLinePosition)/`$(`$MyInvocation.PipeLineLength)) : aucun éléments.`\"}
else
{ Write-Debug `\"Segment $VariableName (`$(`$MyInvocation.PipeLinePosition)/`$(`$MyInvocation.PipeLineLength)) : `$$VariableName éléments trouvés.`\"}
}
\"@
)
}[/code:1]
Quelques tests :
[code:1]
New-Alias ViewSegment Create-ViewSegmentSB
New-Alias VwSg Create-ViewSegmentSB

1..20|&$(ViewSegment \"Premier\"«»)|`
#Filtre les nombres pairs
Where{ !($_ -band 1) }|&$(ViewSegment \"Pairs\"«»)|`
#Filtre les nombres divisible par cinq
Where{ ($_ % 5) -eq 0 }|&$(ViewSegment \"DivisibleParCinq\"«»)
[/code:1]
Affichera, avec $DebugPreference=\"Continue\" :
[code:1]
10
20
DEBUG: Segment Premier (1) : 20 éléments trouvés.
DEBUG: Segment Pairs (3) : 10 éléments trouvés.
DEBUG: Segment DivisibleParCinq (5) : 2 éléments trouvés.
[/code:1]
Les variables utilisés sont locales aux scriptblock :
[code:1]
$Tous=-1
1..20|&$(VwSg \"Tous\"«»)|`
Where{ !($_ -band 1) }|&$(VwSg \"Tous\"«»)|`
Where{ ($_ % 5) -eq 0 }|&$(VwSg \"Tous\"«»)
$Tous #Renvoi -1

$Nom=\"PremierSegment\"
1..20|&$(ViewSegment \"$Nom\"«»)

function Name {\"TestFonction\"}
1..20|&$(ViewSegment (Name))

#Erreur gérées
1..20|&$(ViewSegment)
1..20|&$(ViewSegment \"\"«»)
1..20|&$(ViewSegment \"To/us\"«»)
1..20|&$(ViewSegment \"Un à vingt\"«»)
1..20|&$(ViewSegment \"`$Nom\"«»)
[/code:1]
Et si cela vous paraît tordu, c'est une occasion de réviser la création dynamique de code et son exécution à la volée sous PowerShell. ;-)

Plus loin, plus haut, plus d'aspirine
Compilation à la volée
Génération dynamique de code .NET et Java

[Edit]
Correction de deux erreur de frappe et ajout du calcul du nombre de segment de pipeline.<br><br>Message édité par: Laurent Dardenne, à: 2/02/09 18:24

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.061 secondes
Propulsé par Kunena