Question (Doc] Bloc de script à liaison retardée
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
-
- Messages : 6298
- Remerciements reçus 68
Il s'agit du comportement d'un argument en tant que bloc de script à liaison retardée (delay-bind ScriptBlock).
Quand l'argument d'un paramètre est de type [ScriptBlock], que le paramètre n'est pas déclaré avec le type [System.Objet] ou [ScriptBlock] et que le paramètre récupére l'objet dans le pipeline (ValueFromPipeline=$true), l'exécution de ce ScriptBlock est retardé jusqu'à ce que des données soient fournies dans le pipeline.
L'objet issu du pipeline est passé en argument au ScriptBlock en tant que $_, le résultat de l'exécution du ScriptBlock est ensuite
lié comme argument du paramètre.
Quelques exemples :
[code:1]
Function Test{
param (
[Parameter(Mandatory=$true,ValueFromPipeline = $true)]
[System.Management.Automation.PSObject] $InputObject)
Write-Host $inputObject.GetType().Fullname
$inputObject
}
Get-Date| Test
#Reçoit un objet de type DateTime et affiche la date
Get-Date| Test -inputobject {$_.DayOfWeek}
#Reçoit un objet de type String et affiche le contenu de la propriété DayOfWeek
Function Test1{
param (
[Parameter(Mandatory=$true,ValueFromPipeline = $true)]
[System.Object] $InputObject)
Write-Host $inputObject.GetType().Fullname
$inputObject
}
$O=new-object System.Object
$O|Add-Member NoteProperty DayOfWeek \"Dimanche\"
$O|Add-Member NoteProperty Heure 12
$O| Test1
#Reçoit un objet de type Object et affiche toutes ses propriétés
$O| Test1 -inputobject {$_.DayOfWeek}
#Provoque une erreur de liaison (binding)
#Reçoit un objet de type System.Management.Automation.ScriptBlock et affiche son contenu
Function Test2{
param (
[Parameter(Mandatory=$true,ValueFromPipeline = $true)]
[ScriptBlock] $InputObject)
Write-Host $inputObject.GetType().Fullname
$inputObject
}
$sb={Write-host \"Test\" -fore green}
$sb| Test2
#Reçoit un objet de type System.Management.Automation.ScriptBlock et affiche
# le contenu de l'objet émis dans le pipeline
$sb| Test2 -inputobject {$_.IsFilter}
#Provoque une erreur de liaison (binding)
#Reçoit un objet de type System.Management.Automation.ScriptBlock, mais affiche
# cette fois le contenu de l'argument.
[/code:1]
On peut retrouver ce comportement à l'aide du cmdlet Trace-Command :
[code:1]
Trace-Command parameterbinding {$input| Test -inputobject {$_.DayOfWeek}} -pshost -inputobject (Get-Date)
[/code:1]
Ce qui nous affiche :
Pour ce qui est des fonctions de tests provoquant une erreur, le binding n'est pas retardé :DÉBOGUER : ParameterBinding Information: 0 : BIND NAMED cmd line args [Test]
DÉBOGUER : ParameterBinding Information: 0 : Adding ScriptBlock to delay-bind list for parameter 'InputObject'
DÉBOGUER : ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Test]
DÉBOGUER : ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Test]
DÉBOGUER : ParameterBinding Information: 0 : CALLING BeginProcessing
DÉBOGUER : ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Test]
DÉBOGUER : ParameterBinding Information: 0 : Invoking delay-bind ScriptBlock
DÉBOGUER : ParameterBinding Information: 0 : BIND arg [Thursday] to parameter [InputObject]
DÉBOGUER : ParameterBinding Information: 0 : Executing DATA GENERATION metadata:
[System.Management.Automation.ArgumentTypeConverterAttribute]
DÉBOGUER : ParameterBinding Information: 0 : result returned from DATA GENERATION: Thursday
DÉBOGUER : ParameterBinding Information: 0 : COERCE arg to [System.Management.Automation.PSObject]
DÉBOGUER : ParameterBinding Information: 0 : Parameter and arg types the same, no coercion is needed.
DÉBOGUER : ParameterBinding Information: 0 : BIND arg [Thursday] to param [InputObject] SUCCESSFUL
DÉBOGUER : ParameterBinding Information: 0 : PIPELINE object TYPE = [System.DateTime]
DÉBOGUER : ParameterBinding Information: 0 : RESTORING pipeline parameter's original values
DÉBOGUER : ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Test]
DÉBOGUER : ParameterBinding Information: 0 : CALLING EndProcessing
...
[code:1]
Trace-Command parameterbinding {$input| Test1 -inputobject {$_.DayOfWeek}} -pshost -inputobject $O
[/code:1]
Vous trouverez un exemple dans l'aide en ligne de du cmdlet Select-String.DÉBOGUER : ParameterBinding Information: 0 : BIND NAMED cmd line args [Test1]
DÉBOGUER : ParameterBinding Information: 0 : BIND arg [$_.DayOfWeek] to parameter [InputObject]
DÉBOGUER : ParameterBinding Information: 0 : Executing DATA GENERATION metadata:
[System.Management.Automation.ArgumentTypeConverterAttribute]
DÉBOGUER : ParameterBinding Information: 0 : result returned from DATA GENERATION: $_.DayOfWeek
DÉBOGUER : ParameterBinding Information: 0 : COERCE arg to [System.Object]
DÉBOGUER : ParameterBinding Information: 0 : Parameter and arg types the same, no coercion is needed.
DÉBOGUER : ParameterBinding Information: 0 : BIND arg [$_.DayOfWeek] to param [InputObject] SUCCESSFUL
DÉBOGUER : ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Test1]
DÉBOGUER : ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Test1]
DÉBOGUER : ParameterBinding Information: 0 : CALLING BeginProcessing
...
Pour plus de détails, voir les classes privées suivantes (cf. Reflector):
System.Management.Automation.CmdletParameterBinderController
System.Management.Automation.CmdletParameterBinderController+DelayedScriptBlockArgument
[edit]
Les spécifications de la V2 :
If the parameter type is not object or scriptblock, an argument having type scriptblock is evaluated and its result is passed as the argument's value. (This is known as delayed script block binding.) If the parameter type is object or scriptblock, an argument having type scriptblock is passed as is.
Message édité par: Laurent Dardenne, à: 15/07/10 19:37<br><br>Message édité par: Laurent Dardenne, à: 5/03/14 18:12
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
-
- Messages : 6298
- Remerciements reçus 68
L'objectif est de créer un objet personnalisé puis de l'utiliser avec certains cmdlets.
On paramètre le cmdlet à partir de l'objet émit dans le pipeline.
Un usage classique de new-item :
[code:1]
del C:\Temp\Test_ToDelete.tmp
new-item -path \"C:\Temp\" -name Test_ToDelete.tmp -type \"File\" -value \"Fichier de test.\" -force
Type C:\Temp\Test_ToDelete.tmp
[/code:1]
On peut très bien paramètrer cet appel :
[code:1]
$path=\"C:\Temp\"
$name=\"Test_ToDelete.tmp\"
$Content=\"Fichier de test.\"
new-item -path $Path -name $FileName -type \"File\" -value $Content -Force
Type C:\Temp\Test_ToDelete.tmp
[/code:1]
Si cela vous suffit, pas besoin de lire la suite

De mon coté comme j'utilise le plus souvent des objets personnalisés et le pipeline, ces derniers temps je me suis dit que cela serait bien de pouvoir coupler les deux.
Pour détailler les paramètres d'une commande on utilisera cette fonction . Elle renvoi le résultat suivant :
[code:1]
Get-Parameter New-Item
# ParameterSet: pathSet
#
# Name Type Pos BV BP Aliases Mandatory Dynamic
# ---- ---- --- -- --
# Confirm SwitchParameter Named False False {cf, co} False False
# Credential PSCredential Named False True {cr} False False
# Force SwitchParameter Named False False {f} False False
# ItemType String Named False True {Type, i} False False
# *Path String[] 1 False True {p} True False
# UseTransaction SwitchParameter Named False False {usetx, u} False False
# Value Object Named True True {v} False False
# WhatIf SwitchParameter Named False False {wi, w} False False
#
#
# ParameterSet: nameSet
#
# Name Type Pos BV BP Aliases Mandatory Dynamic
# ---- ---- --- -- --
# Confirm SwitchParameter Named False False {cf, co} False False
# Credential PSCredential Named False True {cr} False False
# Force SwitchParameter Named False False {f} False False
# ItemType String Named False True {Type, i} False False
# *Name String Named False True {n} True False
# *Path String[] 1 False True {p} False False
# UseTransaction SwitchParameter Named False False {usetx, u} False False
# Value Object Named True True {v} False False
# WhatIf SwitchParameter Named False False {wi, w} False False
[/code:1]
Le cmdlet New-Item propose 2 jeux de paramètres, chaque jeux a un paramètre obligatoire et seul le paramètre Value est émit par valeur dans le pipeline .
Tous les paramètres qui nous intéressent (path, name, type et value) sont passés dans le pipeline par nom de propriétés.
A partir de ces informations on peut créer notre objet personnalisé, chaque nom de ses membres correspond à un nom de paramètre du cmdlet ciblé :
[code:1]
$F=New-Object PSObject -Property @{Name=\"Test_ToDelete.tmp\";Path=\"C:\Temp\";Value=\"Fichier de test.\";Type=\"File\"}
[/code:1]
Pour la suite des explications on utilisera le compte-rendu du cmdlet Trace-Command, qui trace l'exécution d'une suite d'instructions.
Les lignes suivantes sont communes, en début de fichier, aux trois exemples à venir :
et les suivantes le sont en fin de fichier :#...
#MANDATORY PARAMETER CHECK on cmdlet [New-Item]
#CALLING BeginProcessing
#BIND PIPELINE object to parameters: [New-Item]
Dans notre exemple on ne gére pas tous les paramètres du cmdlet, Credential par exemple.# Parameter [Credential] PIPELINE INPUT ValueFromPipelineByPropertyName WITH COERCION
#MANDATORY PARAMETER CHECK on cmdlet [New-Item]
#CALLING BeginProcessing
#...
Premier essai, le premier qui vient à l'esprit :
[code:1]
del C:\Temp\Test_ToDelete.tmp
Trace-Command parameterbinding {$F|new-item } -filepath C:\Temp\newitem1.Log #-pshost
[/code:1]
Ici on obtient une exception, car le paramètre Name n'est pas lié :
Ainsi, la liaison utilise le jeux de paramètre nommé pathSet, le moteur ne recherche donc pas à lier le paramètre Name.# Parameter [Value] PIPELINE INPUT ValueFromPipeline NO COERCION
# BIND arg [@{Value=Fichier de test.; Name=Test_ToDelete.tmp; Path=C:\Temp; Type=File}] to param [Value] SUCCESSFUL
# Parameter [Path] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
# BIND arg [System.String[]] to param [Path] SUCCESSFUL
# Parameter [ItemType] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
# BIND arg [File] to param [ItemType] SUCCESSFUL
On peut le savoir en utilisant la trace (cf. Get-Trace) suivante :
[code:1]
Trace-Command ParameterBinderController {$F|new-item -name {$_.name}} -filepath C:\Temp\newitem1-1.Log
[/code:1]
C'est ici que la liaison retardée (delay-bind ScriptBlock) va nous être utile.
En précisant le paramètre Name on force le moteur de PS à utiliser le jeux de paramètre nommé nameSet :
[code:1]
Trace-Command parameterbinding {$F|new-item -name {$_.name} -Force } -filepath C:\Temp\newitem2.Log #-pshost
Type C:\Temp\Test_ToDelete.tmp
[/code:1]
Par contre, on constate que le contenu du fichier n'est pas celui attendu :# Invoking delay-bind ScriptBlock
# BIND arg [Test_ToDelete.tmp] to parameter [Name]
# BIND arg [Test_ToDelete.tmp] to param [Name] SUCCESSFUL
# Parameter [Value] PIPELINE INPUT ValueFromPipeline NO COERCION
# BIND arg [@{Value=Fichier de test.; Name=Test_ToDelete.tmp; Path=C:\Temp; Type=File}] to param [Value] SUCCESSFUL
# Parameter [Path] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
# BIND arg [System.String[]] to param [Path] SUCCESSFUL
# Parameter [ItemType] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
# BIND arg [File] to param [ItemType] SUCCESSFUL
Le paramètre Value étant de type Object, il n'y a pas de conversion de type.@{Value=Fichier de test.; Name=Test_ToDelete.tmp; Path=C:\Temp; Type=File}
On doit redéfinir la méthode ToString() de notre objet personnalisé :
[code:1]
$F|Add-member ScriptMethod ToString {$this.Value} -Force
Trace-Command parameterbinding {$F|new-item -name {$_.name} -Force} -filepath C:\Temp\newitem3.Log #-pshost
Type C:\Temp\Test_ToDelete.tmp
[/code:1]
La lecture du fichier de trace nous confirme que cette fois le résultat est bien celui attendu :
Essayons un autre cmdlet :# Invoking delay-bind ScriptBlock
# BIND arg [Test_ToDelete.tmp] to param [Name] SUCCESSFUL
# Parameter [Value] PIPELINE INPUT ValueFromPipeline NO COERCION
# BIND arg [Fichier de test.] to param [Value] SUCCESSFUL
# Parameter [Path] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
# BIND arg [System.String[]] to param [Path] SUCCESSFUL
# Parameter [ItemType] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
# BIND arg [File] to param [ItemType] SUCCESSFUL
[code:1]
Get-Parameter New-Variable
# ParameterSet: __AllParameterSets
#
# Name Type Pos BV BP Aliases Mandatory Dynamic CommandName
# ---- ---- --- -- --
# PassThru SwitchParameter Named False False {p} False False
# Force SwitchParameter Named False False {f} False False
# Scope String Named False False {s} False False
# Confirm SwitchParameter Named False False {cf, c} False False
# WhatIf SwitchParameter Named False False {wi, w} False False
# Value Object 2 True True {va} False False
# Name String 1 False True {n} True False
# Description String Named False False {d} False False
# Visibility SessionStateEntryVisibility Named False False {vi} False False
# Option ScopedItemOptions Named False False {o} False False
gv T*
Trace-Command parameterbinding {$F|new-Variable } -pshost
gv T*
rv Test_ToDelete.tmp
[/code:1]
Pour ce cmdlet, le binding se fait simplement sans utiliser la liaison retardée.
PowerShell ? Ouai c'est pas mal

[edit]
Un autre exemple :
[code:1]Get-ChildItem -Path *.txt |Rename-Item -NewName {$_.name -replace \".txt$\",\".bat\"} -whatif[/code:1]<br><br>Message édité par: Laurent Dardenne, à: 24/07/10 12:41
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- jojo
-
- Hors Ligne
- Membre elite
-
- Messages : 187
- Remerciements reçus 0
Quand l'argument d'un paramètre est de type [ScriptBlock], que le paramètre n'est pas déclaré avec le type [System.Objet] ou [ScriptBlock] et que le paramètre récupére l'objet dans le pipeline (ValueFromPipeline=$true), l'exécution de ce ScriptBlock est retardé jusqu'à ce que des données soient fournies dans le pipeline.
hello Laurent

merci pour tes explication, mais j'ai pas compris une chose, vous dites que le le paramètre doit récupérer l'objet dans le pipeline (ValueFromPipeline=$true) pour que le delay-bind fonctionne alors que dans mes tests la propriété -property du cmdlet sort-object permet ce genre de binding sans qu'elle accepte une entrée par le pipeline:
[code:1]
PS D:\> Get-Parameter Sort-Object
ParameterSet: __AllParameterSets
Name Type Pos BV BP Aliases Mandatory Dynamic
---- ---- --- -- --
CaseSensitive SwitchParameter Named False False {ca} False False
Culture String Named False False {cu} False False
Descending SwitchParameter Named False False {d} False False
InputObject PSObject Named True False {i} False False
* Property Object[] 1 False False {p} False False
Unique SwitchParameter Named False False {u} False False
PS D:\> Get-Process | Sort-Object -Property {$_.Name[-1]}
# ça fonctionne[/code:1]
pouvez m'éclaircir sur ce point, merci

Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
-
- Messages : 6298
- Remerciements reçus 68
jojo écrit:
Ici, je pense que c'est le code du cmdlet qui gère le scriptblock et pas le parseur. Le delay-bind n'est donc pas déclenché, la régle n'étant pas respectée:pouvez m'éclaircir sur ce point, merci
Ceci je n'ai pas trouvé trace de ce comportement dans les specs publiées de PS.<br><br>Message édité par: Laurent Dardenne, à: 28/09/12 11:49[STA] C:\temp> get-help Sort-Object -parameter property|more
-Property <Object[]>
Specifies the properties to use when sorting. Objects are sorted based on the values of these properties. Enter
the names of the properties. Wildcards are permitted.
If you specify multiple properties, the objects are first sorted by the first property. If more than one object
has the same value for the first property, those objects are sorted by the second property. This process continues
until there are no more specified properties or no groups of objects.
If you do not specify properties, the cmdlet sorts based on default properties for the object type.
The value of the Property parameter can be a calculated property. To create a calculated, property, use a hash
table. Valid keys are:
-- Expression <string> or <script block>
-- Ascending <Boolean>
-- Descending <Boolean>
Obligatoire ? false
Position ? 1
Valeur par défaut Default properties
Accepter l'entrée de pipeline ? false
Accepter les caractères génériques ? true
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- jojo
-
- Hors Ligne
- Membre elite
-
- Messages : 187
- Remerciements reçus 0
donc il ne s'agit pas de delay-bind ScriptBlock

j'ai trouvé un autre code semblable dans le NET, mais j'ai pas trouvé les règles de ce compotement bizzaroide

[code:1]
PS D:\> Get-Parameter Copy-Item
ParameterSet: Path
Name Type Pos BV BP Aliases Mandatory Dynamic
---- ---- --- -- --
Confirm SwitchParameter Named False False {cf, conf} False False
Container SwitchParameter Named False False {cont} False False
Credential PSCredential Named False True {cr} False False
Destination String 2 False True {d} False False
Exclude String[] Named False False {e} False False
Filter String Named False False {fi} False False
Force SwitchParameter Named False False {fo} False False
Include String[] Named False False {i} False False
PassThru SwitchParameter Named False False {pas} False False
*Path String[] 1 True True {pat} True False
Recurse SwitchParameter Named False False {r} False False
UseTransaction SwitchParameter Named False False {usetx, u} False False
WhatIf SwitchParameter Named False False {wi, w} False False
ParameterSet: LiteralPath
Name Type Pos BV BP Aliases Mandatory Dynamic
---- ---- --- -- --
Confirm SwitchParameter Named False False {cf, conf} False False
Container SwitchParameter Named False False {cont} False False
Credential PSCredential Named False True {cr} False False
Destination String 2 False True {d} False False
Exclude String[] Named False False {e} False False
Filter String Named False False {fi} False False
Force SwitchParameter Named False False {fo} False False
Include String[] Named False False {i} False False
*LiteralPath String[] 1 False True {PSPath, l} True False
PassThru SwitchParameter Named False False {p} False False
Recurse SwitchParameter Named False False {r} False False
UseTransaction SwitchParameter Named False False {usetx, u} False False
WhatIf SwitchParameter Named False False {wi, w} False False
PS D:\> Copy-Item -Path $source -Destination $destination -Filter {$_.PSIsContainer} -Recurse -Force[/code:1]
stackoverflow.com/questions/12448513/cre...ll/12455327#12455327
es-ce qu'on peux appeler ce comportement delay-unbind ScriptBlock

es-ce qu'on peux \"l'emuler\" dans nos fonctions avancées ?
Connexion ou Créer un compte pour participer à la conversation.
- Laurent Dardenne
- Auteur du sujet
- Hors Ligne
- Modérateur
-
- Messages : 6298
- Remerciements reçus 68
Je ne sais pas, faut le temps d'étudier ce cas.est-ce qu'on peux appeler ce comportement delay-unbind ScriptBlock
jojo écrit:
C'est le parseur qui s'en charge, enfin à vérifier dans les exemples précédents.est-ce qu'on peut \"l'émuler\" dans nos fonctions avancées ?
Tutoriels PowerShell
Connexion ou Créer un compte pour participer à la conversation.
- Vous êtes ici :
-
Accueil
-
forum
-
PowerShell
-
Contributions à la communauté
- (Doc] Bloc de script à liaison retardée