80 votes

Quel est le style de codage recommandé pour PowerShell?

Est-il recommandé de style de codage comment écrire des scripts PowerShell? C'est pas sur la structure du code (nombre de fonctions, si pour utiliser le module, ...). C'est à propos de"comment écrire le code afin qu'il soit lisible'.

Dans les langages de programmation il y a quelques recommandé de codage styles (de quoi tiret, comment tiret - espaces/tabulations, où faire de nouvelle ligne, où mettre des accolades,...), mais je n'ai pas vu toute suggestion pour PowerShell.

Ce qui m'intéresse particulièrement:


Comment écrire les paramètres

function New-XYZItem
  ( [string] $ItemName
  , [scriptblock] $definition
  ) { ...

(Je vois que c'est plus comme 'V1' la syntaxe) ou

function New-PSClass  {
  param([string] $ClassName
       ,[scriptblock] $definition
  )...

ou (pourquoi ajouter un attribut vide?)

function New-PSClass  {
  param([Parameter()][string] $ClassName
       ,[Parameter()][scriptblock] $definition
  )...

ou (autre mise en forme, j'ai vu peut-être dans Jaykul du code)

function New-PSClass {
  param(
        [Parameter()]
        [string]
        $ClassName
        ,
        [Parameter()]
        [scriptblock]
        $definition
  )...

ou ..?


Comment écrire complexe pipeline

Get-SomeData -param1 abc -param2 xyz | % {
    $temp1 = $_
    1..100 | % { 
      Process-somehow $temp1 $_
    }
  } | % { 
    Process-Again $_
  } | 
  Sort-Object -desc

ou (nom de l'applet de commande sur la ligne nouvelle)

Get-SomeData -param1 abc -param2 xyz | 
  % {
    $temp1 = $_
    1..100 | 
      % { 
        Process-somehow $temp1 $_
      }
  } | 
  % { 
    Process-Again $_
  } | 
  Sort-Object -desc |

et si il y a -commencer le processus de fin d'params? comment le rendre le plus lisible?

Get-SomeData -param1 abc -param2 xyz | 
  % -begin {
     init
  } -process {
     Process-somehow2 ...
  } -end {
     Process-somehow3 ...
  } |
  % -begin {
  } ....

ou

Get-SomeData -param1 abc -param2 xyz | 
  %  `
    -begin {
      init
    } `
    -process {
      Process-somehow2 ...
    } `
    -end {
      Process-somehow3 ...
    } |
  % -begin {
  } ....

le indentitation est important ici, et ce que l'élément est mis sur la nouvelle ligne.


J'ai couvert que les questions qui viennent à mon esprit très fréquemment. Il y a quelques autres, mais je tiens à garder ce DONC, la question "court".

Toutes les autres suggestions sont les bienvenus.

91voto

Richard Berg Points 14218

Après avoir passé quelques années à plonger dans Powershell v2.0, voici ce sur quoi je me suis installé:

 <#
.SYNOPSIS
Cmdlet help is awesome.  Autogenerate via template so I never forget.

.DESCRIPTION
.PARAMETER
.PARAMETER
.INPUTS
.OUTPUTS
.EXAMPLE
.EXAMPLE
.LINK
#>
function Get-Widget
{
    [CmdletBinding()]
    param (
        # Think about which params users might loop over.  If there is a clear
        # favorite (80/20 rule), make it ValueFromPipeline and name it InputObject.
        [parameter(ValueFromPipeline=$True)]
        [alias("Server")]
        [string]$InputObject,

        # All other loop candidates are marked pipeline-able by property name.  Use Aliases to ensure the most 
        # common objects users want to feed in will "just work".
        [parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$True)]
        [alias("FullName")]
        [alias("Path")]
        [string[]]$Name,

        # Provide & document defaults for optional params whenever possible.
        [parameter(Position=1)]
        [int]$Minimum = 0,

        [parameter(Position=2)]
        [int]$ComputerName = "localhost",

        # Stick to standardized parameter names when possible.  *Especially* with switches.  Use Aliases to support 
        # domain-specific terminology and/or when you want to expose the parameter name of the .Net API you're wrapping.
        [parameter()]
        [Alias("IncludeFlibbles")]
        [switch]$All,
    )

    # The three main function blocks use this format if & only if they are short one-liners    
    begin { $buf = new-list string }

    # Otherwise they use spacing comparable to a C# method
    process    
    {
        # Likewise, control flow statements have a special style for one-liners
        try
        {
            # Side Note: internal variables (which may be inherited from a parent scope)  
            # are lowerCamelCase.  Direct parameters are UpperCamelCase.
            if ($All)
                { $flibbles = $Name | Get-Flibble }   
            elseif ($Minimum -eq 0)          
                { $flibbles = @() }
            else
                { return }                       

            $path = $Name |
                ? { $_.Length -gt $Minimum } |
                % { $InputObject.InvokeGetAPI($_, $flibbles) } |
                ConvertTo-FullPath
        }
        finally { Cleanup }

        # In general, though, control flow statements also stick to the C# style guidelines
        while($true)
        {
            Do-Something
            if ($true)
            {
                try
                {
                    Do-Something
                    Do-Something
                    $buf.Add("abc")
                }
                catch
                {
                    Do-Something
                    Do-Something
                }
            }            
        }    
    }    
}

<# 
Pipelines are a form of control flow, of course, and in my opinion the most important.  Let's go 
into more detail.

I find my code looks more consistent when I use the pipeline to nudge all of Powershell's supported 
language constructs (within reason) toward an "infix" style, regardless of their legacy origin.  At the 
same time, I get really strict about avoiding complexity within each line.  My style encourages a long,
consistent "flow" of command-to-command-to-command, so we can ensure ample whitespace while remaining
quite compact for a .Net language. 

Note - from here on out I use aliases for the most common pipeline-aware cmdlets in my stable of 
tools.  Quick extract from my "meta-script" module definition:
sal ?? Invoke-Coalescing
sal ?: Invoke-Ternary
sal im Invoke-Method
sal gpv Get-PropertyValue
sal spv Set-PropertyValue
sal tp Test-Path2
sal so Select-Object2        
sal eo Expand-Object        

% and ? are your familiar friends.
Anything else that begins with a ? is a pseudo-infix operator autogenerated from the Posh syntax reference.
#>        
function PipelineExamples
{
    # Only the very simplest pipes get to be one-liners:
    $profileInfo = dir $profile | so @{Path="fullname"; KBs={$_.length/1kb}}
    $notNull = $someString | ?? ""        
    $type = $InputObject -is [Type] | ?: $InputObject $InputObject.GetType()        
    $ComObject | spv Enabled $true
    $foo | im PrivateAPI($param1, $param2)
    if ($path | tp -Unc)
        { Do-Something }

    # Any time the LHS is a collection (i.e. we're going to loop), the pipe character ends the line, even 
    # when the expression looks simple.
    $verySlowConcat = ""            
    $buf |
        % { $verySlowConcat += $_ }
    # Always put a comment on pipelines that have uncaptured output [destined for the caller's pipeline]
    $buf |
        ? { $_ -like "*a*" }


    # Multi-line blocks inside a pipeline:
    $orders |
        ? { 
            $_.SaleDate -gt $thisQuarter -and
            ($_ | Get-Customer | Test-Profitable) -and
            $_.TastesGreat -and
            $_.LessFilling
        } |
        so Widgets |        
        % {                
            if ($ReviewCompetition)
            {
                $otherFirms |
                    Get-Factory |
                    Get-ManufactureHistory -Filter $_ |
                    so HistoryEntry.Items.Widgets                     
            }
            else
            {
                $_
            }
        } |            
        Publish-WidgetReport -Format HTML


    # Mix COM, reflection, native commands, etc seamlessly
    $flibble = Get-WmiObject SomethingReallyOpaque |
        spv AuthFlags 0xf -PassThru |
        im Put() -PassThru |
        gpv Flibbles |
        select -first 1

    # The coalescing operator is particularly well suited to this sort of thing
    $initializeMe = $OptionalParam |
        ?? $MandatoryParam.PropertyThatMightBeNullOrEmpty |
        ?? { pwd | Get-Something -Mode Expensive } |
        ?? { throw "Unable to determine your blahblah" }           
    $uncFolderPath = $someInput |
        Convert-Path -ea 0 |
        ?? $fallback { tp -Unc -Folder }

    # String manipulation        
    $myName = "First{0}   Last{1}   " |
        ?+ "Suffix{2}" |
        ?replace "{", ": {" |
        ?f {eo richard berg jr | im ToUpper}            

    # Math algorithms written in this style start to approach the elegance of functional languages
    $weightedAvg = $values |
        Linq-Zip $weights {$args[0] * $args[1]} |
        Linq-Sum |
        ?/ ($weights | Linq-Sum)
}

# Don't be afraid to define helper functions.  Thanks to the script:Name syntax, you don't have to cram them into 
# the begin{} block or anything like that.  Name, params, etc don't always need to follow the cmdlet guidelines.
# Note that variables from outer scopes are automatically available.  (even if we're in another file!)
function script:Cleanup { $buf.Clear() }

# In these small helpers where the logic is straightforward and the correct behavior well known, I occasionally 
# condense the indentation to something in between the "one liner" and "Microsoft C# guideline" styles
filter script:FixComputerName
{
    if ($ComputerName -and $_) {            
        # handle UNC paths 
        if ($_[1] -eq "\") {   
            $uncHost = ($_ -split "\\")[2]
            $_.Replace($uncHost, $ComputerName)
        } else {
            $drive = $_[0]
            $pathUnderDrive = $_.Remove(0,3)            
            "\\$ComputerName\$drive`$\$pathUnderDrive"
        }
    } else {
        $_
    }
}
 

Oups, ça a pris plus de temps que prévu. J'espère que les réponses que vous vouliez sont là quelque part :)

edit - Le surligneur de la syntaxe de StackOverflow m'abandonne complètement. Collez-le dans l'ISE.

8voto

Tohuw Points 131

Je suis récemment tombé sur un excellent point sur le retrait de style dans PowerShell. Comme le commentaire connexe unis, observer la différence entre ces mêmes syntaxes:

1..10 | Sort-Object 
{
    -$_
}

et

1..10 | Sort-Object {
    -$_
}

Alors que mon inclination est de "faire comme les Romains" et l'utilisation de la norme C# style d'indentation (Allman, plus ou moins), je conteste cette exception et d'autres similaires.

Cela me porte personnellement à utiliser mon favorisée 1TBS, mais je pourrais être convaincu du contraire. Comment avez-vous régler, par curiosité?

5voto

Mad Tom Vane Points 1146

Je sais que la question est ancienne, mais Don Jones 12 Meilleures pratiques PowerShell . Juste en lisant ces 12 meilleures pratiques, j'ai amélioré mes scripts de 100%. Avant ils étaient vraiment mauvais. Vraiment mauvais. Lentement les nettoyer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X