Quel est le style de codage recommandé pour PowerShell?

Existe-t-il un style de codage recommandé pour écrire des scripts PowerShell? Il ne s’agit pas de structurer le code (combien de fonctions, si utiliser le module, …). Il s’agit de « comment écrire le code pour qu’il soit lisible ».

Dans les langages de programmation, il existe des styles de codage recommandés (quoi mettre en retrait , comment indenter des espaces / tabulations, où créer une nouvelle ligne , où placer des accolades , …), mais je n’ai vu aucune suggestion pour PowerShell.

Ce qui m’intéresse particulièrement dans:


Comment écrire des parameters

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

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

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

ou (pourquoi append un atsortingbut vide?)

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

ou (autre format que j’ai vu peut-être dans le code de Jaykul)

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

ou ..?


Comment écrire un pipeline complexe

 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 nouvelle ligne)

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

et s’il y a -begin -process -end 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 { } .... 

l’indentation est importante ici et quel élément est mis sur la nouvelle ligne.


Je n’ai couvert que des questions qui me viennent très souvent à l’esprit. Il y en a d’autres, mais j’aimerais que cette question SO soit «courte».

N’importe quelles autres suggestions sont les bienvenues.

Après avoir passé quelques années à plonger profondément dans Powershell v2.0, voici ce que j’ai choisi:

 < # .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")] [ssortingng]$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")] [ssortingng[]]$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 ssortingng } # 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 = $someSsortingng | ?? "" $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 (ie 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 } # Ssortingng 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 duré plus longtemps que prévu. J’espère que les réponses que vous vouliez sont là quelque part 🙂

edit – Le surligneur de syntaxe de StackOverflow me lâche complètement. Collez-le dans l’ISE.

Je suis récemment tombé sur un excellent point concernant le style de retrait dans PowerShell . Comme l’indique le commentaire lié, observez la différence entre ces mêmes syntaxes:

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

et

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

Bien que je sois enclin à “faire comme les Romains” et à utiliser le style d’indentation C # standard ( Allman , plus ou moins), je ne suis pas d’accord avec cette exception et d’autres similaires.

Cela me pousse personnellement à utiliser mon 1TBS préféré, mais je pourrais être convaincu du contraire. Comment vous êtes-vous installé, par curiosité?

Pour mémoire, il existe également le Guide des meilleures pratiques et styles de PowerShell .

C’est loin d’être officiel, mais a plusieurs bons points.