Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 3 sur 3
  1. #1
    Membre Expert
    Avatar de I'm_HERE
    Homme Profil pro Walid Toumi
    Inscrit en
    juillet 2008
    Messages
    964
    Détails du profil
    Informations personnelles :
    Nom : Homme Walid Toumi
    Localisation : Tunisie

    Informations forums :
    Inscription : juillet 2008
    Messages : 964
    Points : 1 756
    Points
    1 756

    Par défaut Astuce: traduire des directives en des paramètres dynamiques

    salut,

    Ce matin en regardant le site powershell-scripting.com je me suis aperçu que Laurent a des contributions, l'une d'elles est la création de "directives conditionnelles", alors je mes suis dis: "tiens c'est une bonne idée ça !" alors je me suis inspiré de son dernier topic pour créer une fonction se basant sur les directives...et tant qu'on y est... je vais copiez son intro

    <DEBUT_COPIE>
    le principe est de délimiter une ou plusieurs lignes de code par un mot clé de début de directive et

    un mot clé de fin de directive...

    <FIN_COPIE>

    le mot clé de debut étant <#DynParam et le mot clé de fin est Dynparam#>

    malheureusement on ne trouve pas beaucoup de documentation au sujet des paramètres dynamiques, alors on va commencer par différencier les paramètres dynamiques des paramètres statiques:

    les paramètres statiques:
    ils sont parser avant l'execution du code 'parse time'
    ils ne permettent pas de voir les autres paramètres dans le script ou dans la fonction
    ils sont visible dans le systeme d'aide (get-help)

    les paramètres dynamiques:
    ils sont parser dynamiquement à la volée 'runtime'
    ils peuvent voir les autres paramètres
    ils ne sont pas visibles dans le systeme d'aide (get-help)

    pour voir les cmdlets qui ont des paramètres dynamiques:


    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    :exitfor foreach($cmd in Get-Command -CommandType cmdlet) {
      foreach($param in $cmd.ParameterSets | select -Expand parameters) {
          if($param.IsDynamic) {
             New-Object psobject -Property @{
                Command = $cmd
                Provider = $PWD.Provider.Name
             }
             continue exitfor
          }
      }
    }
    Code :
    1
    2
    3
    4
    5
    Provider                                Command
    --------                                -------
    FileSystem                              Add-Content
    FileSystem                              Get-Content
    FileSystem                              Set-Content
    pour voir ces paramètres dans chaque psprovider vous pouvez utiliser ce bout de code dans chaque psprovider:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    foreach($cmd in Get-Command -CommandType cmdlet) {
      $array=@()
      foreach($param in $cmd.ParameterSets | select -ExpandProperty parameters) {
          if($param.IsDynamic) {
               $array+=$param.name
          }
      }
         if($array) 
         {
              new-object psobject -Property @{
                Command =  $cmd.name
                DynamicParameter = $array | select -Unique
                Provider = $PWD.Provider.Name
              }
         }
    }
    Code :
    1
    2
    3
    4
    5
    DynamicParameter           Provider                   Command
    ----------------           --------                   -------
    Encoding                   FileSystem                 Add-Content
    {Delimiter, Wait, Encod... FileSystem                 Get-Content
    Encoding                   FileSystem                 Set-Content
    revenons maintenant à notre fonction:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    #################################################################################################
    # 
    #  Description:
    #     Converti une directive de création de paramètres dynamique en 
    #     code .NET
    #
    #  Version:
    #     1.0
    #
    #  Auteur:
    #     Walid Toumi
    #
    #  Blog:
    #     http://walid-toumi.blogspot.com/
    #
    #  NOTE:
    #    1) pour le moment cette version ne s'utilise qu'avec ISE, vous pouvez copier votre fonction à convertir 
    #       dans le volet de script courant puis appelez la fonction 'ConvertTo-DynamicParam'
    #
    #    2) cette fonction n'est pas complète donc à ne pas utiliser dans des environements de production
    #
    ###################################################{#############################################
    function ConvertTo-DynamicParam {
    
    if([string]::IsNullOrEmpty($psise)) {
        throw 'cette fonction requiert "ISE"'
     }
    $dp=''
    $content = $psISE.CurrentFile.Editor.Text
    # $content =  [IO.FILE]::ReadAllText((join-path $pwd .\testdyn.ps1))
    $dyncomm = @([management.automation.psparser]::Tokenize($content,[ref]$null) | 
        Where { 
            $_.Type -eq 'comment' -and $_.Content -match '<#Dynparam'
              })[0].Content -replace '<#Dynparam.*|Dynparam#>'
    $tmpfunc = Set-Item -Path function:dummyfunc -Value $dyncomm -PassThru
    Remove-Item function:dummyfunc -Force
    $commun=[Management.Automation.Internal.CommonParameters].GetProperties() | foreach { $_.Name }
    $params=@($tmpfunc.Parameters.GetEnumerator() | ? { $commun -notcontains $_.key })
    $ParameterName = $params[0].Value.Name
    $aliases = @($params[0].value.Aliases)
    $attr = $params[0].Value.attributes | Where { $_.TypeId -like '*ParameterAttribute' }
    $vals = $params[0].Value.Attributes | Where { $_.TypeId -notmatch 'ParameterAttribute|ArgumentTypeConverterAttribute' }
    $Mandatory = $attr.Mandatory
    $ParameterSetName=$attr.ParameterSetName
    $Position=$attr.Position
    $ByValue=$attr.ValueFromPipeline
    $ByPropertyName=$attr.ValueFromPipelineByPropertyName
    $RemainingArgs=$attr.ValueFromRemainingArguments
    $Type=$params[0].value.ParameterType.fullname
        $dp+=@"
     DynamicParam { `n
     #if(une condition) {
    `$Attributes = New-Object 'Management.Automation.ParameterAttribute' 
    `$Attributes.ParameterSetName = '$ParameterSetName'            
    `$Attributes.Mandatory = `$$Mandatory
    `$Attributes.Position = $Position
    `$Attributes.ValueFromPipeline = `$$ByValue
    `$Attributes.ValueFromPipelineByPropertyName = `$$ByPropertyName
    `$Attributes.ValueFromRemainingArguments = `$$RemainingArgs
    `$Attributes.HelpMessage = '$helpMessage '
    `$AttributeCollection = New-Object 'Collections.ObjectModel.Collection[Attribute]' 
    `$AttributeCollection.Add(`$Attributes) `n
    "@
    if($aliases) {
       $dp+=@"
    `$Attribute=new-object 'System.Management.Automation.AliasAttribute' $($aliases -join ',')
    `$attributeCollection.Add(`$Attribute) `n
    "@
    } 
    # attributs de validation
       if($Vals) {
          foreach($Val in $Vals) {
             Switch ($val.TypeId.Name) {
               'ValidateScriptAttribute' 
                 { 
         $dp+=@"          
    `$Attribute=new-object 'Management.Automation.ValidateScriptAttribute' {`$$($Val.ScriptBlock)} 
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 }
               'ValidatePatternAttribute'
                 { 
        $dp+=@"            
    `$Attribute=new-object 'Management.Automation.ValidatePatternAttribute' '$($Val.RegexPattern)' 
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 }
               'ValidateSetAttribute'
                 {
        $dp+=@"           
    `$Attribute = new-object System.Management.Automation.ValidateSetAttribute $($Val.ValidValues -join ',')
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 }
               'ValidateLengthAttribute'
                 {
        $dp+=@"           
    `$Attribute = new-object System.Management.Automation.ValidateLengthAttribute $($Val.MinLength,$Val.MaxLength -join ',')
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 } 
               'ValidateCountAttribute'
                 {
        $dp+=@"           
    `$Attribute = new-object System.Management.Automation.ValidateCountAttribute $($Val.MinLength,$Val.MaxLength -join ',')
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 }
               'ValidateRangeAttribute'
                 {
        $dp+=@"           
    `$Attribute = new-object System.Management.Automation.ValidateRangeAttribute $($Val.MinRange,$Val.MaxRange -join ',')
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 }
               'ValidateNotNullOrEmptyAttribute'
                 {
        $dp+=@"           
    `$Attribute = new-object System.Management.Automation.ValidateNotNullOrEmptyAttribute
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 }
              'AllowEmptyCollectionAttribute'
                 {
        $dp+=@"           
    `$Attribute = new-object System.Management.Automation.AllowEmptyCollectionAttribute
    `$attributeCollection.Add(`$Attribute) `n
    "@
                 }
             'AllowEmptyStringAttribute'
                 {
        $dp+=@"           
    `$Attribute = new-object System.Management.Automation.AllowEmptyStringAttribute
    `$attributeCollection.Add(`$Attribute) `n
    "@ 
                }
             } #switch
          }#foreach vals
       }#if 
     $dp+=@"
    `$MyParam = @{            
                Typ = 'Management.Automation.RuntimeDefinedParameter'            
                Arg = @(            
                    '$ParameterName'          
                    '$Type'          
                    ,`$AttributeCollection         
                )            
            }  
    `$DynamicParam = New-Object @MyParam
    `$ParamDict = New-Object 'Management.Automation.RuntimeDefinedParameterDictionary'          
    `$ParamDict.Add("$ParameterName", `$DynamicParam)
    `$ParamDict 
     # endif condition }
     
     # vous pouvez testez la présence du paramètre dans le corps de la fonction dynamque par: 
     #
     # if(`$PSBoundParameters.ContainsKey('$ParameterName')) {
     #    // votre code ici 
     #    // vous pouvez utiliser votre variable dynamique comme ceci: `$PSBoundParameters.$ParameterName
     # } 
    }`n
    "@
    $tmpfile =  ([IO.Path]::GetTempFileName()) + '.ps1'
    [regex]::Replace($content,'(?si)<#DynParam.+?DynParam#>',$dp) | Out-File $tmpfile -Force
    ise $tmpfile
    write-host "la fonction converti est dans le fichier: $tmpfile" -ForegroundColor green
    }
    vous verrez que le principe est simple, mais comment ça marche ?

    on met notre fonction dans le volet de script de 'ISE' et on appel notre convertTo-DynamicParam,

    celle-ci va traduire notre 'code Powershell' en du code '.NET' et va créer un fichier dans le 'TEMP'

    contenant notre fonction transformé..

    voici un exemple d'utilisation:

    on créer une simple fonction, ou disant le corps de notre fonction:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function get-simple {
     [cmdletBinding()]
     param(
       [parameter(mandatory=$true)]
       [string]$String
     )
     End {
       
     }
    }

    maintenant on créer nos directives et une clause PARAM qui contiendra notre paramètre dynamique:


    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function get-simple {
     [cmdletBinding()]
     param(
       [parameter(mandatory=$true)]
       [string]$String
     )
    <#DynParam
     param(
      [parameter()]
      [Switch]$Reverse
     )
    DynParam#>
     End {
       
     }
    }

    maintenant on copie notre fonction dans ISE, on appel la fonction 'convertTo-DynamicParam' un autre

    volet va s'afficher contenant ce code:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    function get-simple {
     [cmdletBinding()]
     param(
       [parameter(mandatory=$true)]
       [string]$String
     )
     DynamicParam { 
    
     #if(une condition) {
    $Attributes = New-Object 'Management.Automation.ParameterAttribute' 
    $Attributes.ParameterSetName = '__AllParameterSets'            
    $Attributes.Mandatory = $False
    $Attributes.Position = -2147483648
    $Attributes.ValueFromPipeline = $False
    $Attributes.ValueFromPipelineByPropertyName = $False
    $Attributes.ValueFromRemainingArguments = $False
    $Attributes.HelpMessage = ' '
    $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[Attribute]' 
    $AttributeCollection.Add($Attributes) 
    $MyParam = @{            
                Typ = 'Management.Automation.RuntimeDefinedParameter'            
                Arg = @(            
                    'Reverse'          
                    'System.Management.Automation.SwitchParameter'          
                    ,$AttributeCollection         
                )            
            }  
    $DynamicParam = New-Object @MyParam
    $ParamDict = New-Object 'Management.Automation.RuntimeDefinedParameterDictionary'          
    $ParamDict.Add("Reverse", $DynamicParam)
    $ParamDict 
     # endif condition }
     
     # vous pouvez testez la présence du paramètre dans le corps de la fonction dynamque par: 
     #
     # if($PSBoundParameters.ContainsKey('Reverse')) {
     #    // votre code ici 
     #    // vous pouvez utiliser votre variable dynamique comme ceci: $PSBoundParameters.Reverse
     # } 
    }
    
     End {
       
     }
    }

    on décommente if(condition) { et endif condition } et met l'instruction qu'on veux, par exemple:

    Code :
    if($String -notmatch '\d') {
    ensuite, dans le corps de notre fonction (dans notre cas la partie "END") on teste la présence de notre switch $Reverse:

    Code :
    1
    2
    3
    4
    5
    End {
      if($PSBoundParameters.ContainsKey('Reverse')) {
         
      } 
    }
    puis on écris notre code, par exemple:
    Code :
    1
    2
    3
    4
    5
    6
    7
    End {
      if($PSBoundParameters.ContainsKey('Reverse')) {
          return  -join [regex]::Matches($String,".",'RightToLeft')  
      } 
      $String
    }
    et c'est tout:


    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    function get-simple {
     [cmdletBinding()]
     param(
       [parameter(mandatory=$true)]
       [string]$String
     )
     DynamicParam { 
    
     if($String -notmatch '\d') {
    $Attributes = New-Object 'Management.Automation.ParameterAttribute' 
    $Attributes.ParameterSetName = '__AllParameterSets'            
    $Attributes.Mandatory = $False
    $Attributes.Position = -2147483648
    $Attributes.ValueFromPipeline = $False
    $Attributes.ValueFromPipelineByPropertyName = $False
    $Attributes.ValueFromRemainingArguments = $False
    $Attributes.HelpMessage = ' '
    $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[Attribute]' 
    $AttributeCollection.Add($Attributes) 
    $MyParameter = @{            
                TypeName = 'Management.Automation.RuntimeDefinedParameter'            
                ArgumentList = @(            
                    'Reverse'          
                    'System.Management.Automation.SwitchParameter'          
                    ,$AttributeCollection         
                )            
            }  
    $Dynamic = New-Object @MyParameter 
    $ParamDictionary = New-Object 'Management.Automation.RuntimeDefinedParameterDictionary'          
    $ParamDictionary.Add("Reverse", $Dynamic)
    $ParamDictionary 
    }
     
     # dans le corps de la fonction vous pouvez testez la présence du paramètres
     # dynamque par: 
     #
     # if($PSBoundParameters.ContainsKey('Reverse')) {
     #      // votre code 
     #      // vous pouvez utiliser votre variable dynamique ainsi: $PSBoundParameters.Reverse
     # } 
    }
    
     End {
         if($PSBoundParameters.ContainsKey('Reverse')) {
          return  -join [regex]::Matches($String,".",'RightToLeft')  
      } 
      $String
     }
    }
    Code :
    1
    2
    3
    4
    5
    PS II> get-simple 'string 23' -Reverse
    # ERREUR
    PS II> get-simple "developpez.com" -Reverse
    moc.zeppoleved

  2. #2
    Rédacteur


    Inscrit en
    janvier 2003
    Messages
    6 422
    Détails du profil
    Informations forums :
    Inscription : janvier 2003
    Messages : 6 422
    Points : 14 422
    Points
    14 422

    Par défaut

    Salut Walid,
    tu nous proposes un complément intéressant. Je comptais à l'origine étendre la fonction en intégrant de la substitution, mais soit on crée autant de mots clé que de traitement dédié soit on implémente des templates.

    Je ne l'ai pas implémenté car je m'éloignais du besoin de base, quant au template il existe sous Visual Studio avec T4 (Text Template Transformation Toolkit, cf. http://msdn.microsoft.com/fr-fr/libr...v=vs.100).aspx ), et là on est plus côté développement qu'administration.

    VS proposant un outil en ligne de commande (TextTransform.exe, http://msdn.microsoft.com/en-us/library/bb126245.aspx), autant l'utiliser, c'était la prochaine étape

    La fonction Remove-Conditonnal, ne fait que filtrer du code et n'a pas pour objectif d'être un préprocessor ou un DSL, mais si on m'en proposait un natif je serais ravi !

  3. #3
    Membre Expert
    Avatar de I'm_HERE
    Homme Profil pro Walid Toumi
    Inscrit en
    juillet 2008
    Messages
    964
    Détails du profil
    Informations personnelles :
    Nom : Homme Walid Toumi
    Localisation : Tunisie

    Informations forums :
    Inscription : juillet 2008
    Messages : 964
    Points : 1 756
    Points
    1 756

    Par défaut

    salut Laurent et merci pour ta contribution,

    voici une suite du premier post,


    prenons cette petite fonction:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function Test-Scriptblocks {   
       [cmdletbinding()]
        Param (
          [parameter(ValueFromPipeLine=$true)]
           $Bypipeline
          ,$ByArg = $env:USERNAME)
       End {
        "EndBlock: $ByArg" | Write-Host 
       }
       Begin{
        "BeginBlock"
       }
       DynamicParam { 
        "DynamicParam:ByArgs: $($PSBoundParameters['ByArg'])" |write-host 
        "DynamicParam:ByPipeLine: $($PSBoundParameters['ByPipeline'])" |write-host 
       }
       process {
        "ProcessBlock $ByPipeline" | Write-Host
       }
    }
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    PS II> 1..5 | Test-Scriptblocks
    DynamicParam:ByArgs: 
    DynamicParam:ByPipeLine: 
    BeginBlock
    ProcessBlock 1
    ProcessBlock 2
    ProcessBlock 3
    ProcessBlock 4
    ProcessBlock 5
    EndBlock: Walid

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    PS II> 1..5 | Test-Scriptblocks -ByArg SALAM
    DynamicParam:ByArgs: SALAM
    DynamicParam:ByPipeLine: 
    BeginBlock
    ProcessBlock 1
    ProcessBlock 2
    ProcessBlock 3
    ProcessBlock 4
    ProcessBlock 5
    EndBlock: SALAM
    comme vous voyez cette petite fonction nous montre plusieurs aspects sur le comportement du bloc DynamicParam:

    1- on peux déplacer notre scriptblock DynamicParam ou on veux dans la fonction

    2- si le paramètre statique est initialisé alors il sera évalué avant son evaluation dans le bloc DynamicParam

    3- il faut mettre explicitement au moins un bloc (begin ou End ou Process) avec le bloc DynamicParam

    4- le paramètre $Bypipeline n'est pas visible dans le bloc DynamicParam, il faut utiliser une fonction proxy couplé à un bloc DynamicParam pour manipuler le pipeline avec la technique du SteppablePipeline

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •