¿Existe algún estilo de codificación recomendado sobre cómo escribir scripts de PowerShell?
No se trata de cómo estructurar el código (cuántas funciones, si usar el módulo, ...). Se trata de ' cómo escribir el código para que sea legible '.
En los lenguajes de programación hay algunos estilos de codificación recomendados (qué sangrar , cómo sangrar: espacios / tabulaciones, dónde hacer una nueva línea , dónde poner llaves , ...), pero no he visto ninguna sugerencia para PowerShell.
Me interesa particularmente:
Cómo escribir parámetros
function New-XYZItem
( [string] $ItemName
, [scriptblock] $definition
) { ...
(Veo que es más como sintaxis 'V1')
o
function New-PSClass {
param([string] $ClassName
,[scriptblock] $definition
)...
o (¿por qué agregar un atributo vacío?)
function New-PSClass {
param([Parameter()][string] $ClassName
,[Parameter()][scriptblock] $definition
)...
o (otro formato que vi tal vez en el código de Jaykul)
function New-PSClass {
param(
[Parameter()]
[string]
$ClassName
,
[Parameter()]
[scriptblock]
$definition
)...
o ...?
Cómo escribir una canalización compleja
Get-SomeData -param1 abc -param2 xyz | % {
$temp1 = $_
1..100 | % {
Process-somehow $temp1 $_
}
} | % {
Process-Again $_
} |
Sort-Object -desc
o (nombre del cmdlet en la nueva línea)
Get-SomeData -param1 abc -param2 xyz |
% {
$temp1 = $_
1..100 |
% {
Process-somehow $temp1 $_
}
} |
% {
Process-Again $_
} |
Sort-Object -desc |
¿Y si existen -begin
, -process
y -end
los parámetros? ¿Cómo lo hago más legible?
Get-SomeData -param1 abc -param2 xyz |
% -begin {
init
} -process {
Process-somehow2 ...
} -end {
Process-somehow3 ...
} |
% -begin {
} ....
o
Get-SomeData -param1 abc -param2 xyz |
% `
-begin {
init
} `
-process {
Process-somehow2 ...
} `
-end {
Process-somehow3 ...
} |
% -begin {
} ....
La sangría es importante aquí y también qué elemento se coloca en la nueva línea.
He cubierto solo las preguntas que me vienen a la mente con mucha frecuencia. Hay algunos otros, pero me gustaría que esta pregunta de Stack Overflow sea 'breve'.
Cualquier otra sugerencia es bienvenida.
fuente
Respuestas:
Después de pasar un par de años sumergiéndome bastante en PowerShell v2.0, esto es lo que me decidí:
<# .SYNOPSIS Cmdlet help is awesome. Autogenerate via a template so I never forget. .DESCRIPTION .PARAMETER .PARAMETER .INPUTS .OUTPUTS .EXAMPLE .EXAMPLE .LINK #> function Get-Widget { [CmdletBinding()] param ( # Think about which parameters 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 and document defaults for optional parameters 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, parameters, 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 { $_ } }
El resaltador de sintaxis de Stack Overflow me está abandonando por completo. Péguelo en el ISE.
fuente
Creo que el recurso de estilo de codificación más completo para PowerShell sigue siendo la Guía de estilo y prácticas recomendadas de PowerShell .
De su introducción:
También pusieron a disposición estos enlaces de GitBook :
fuente
Recientemente encontré un punto excelente sobre el estilo de sangría en PowerShell . Como dice el comentario vinculado, observe la diferencia entre estas mismas sintaxis:
1..10 | Sort-Object { -$_ }
y
1..10 | Sort-Object { -$_ }
Si bien mi inclinación es "hacer lo que hacen los romanos" y usar el estilo de sangría estándar de C # ( Allman , más o menos), discrepo con esta excepción y otras similares.
Esto me inclina personalmente a usar mi 1TBS favorito , pero podría estar convencido de lo contrario. ¿Cómo te conformaste, por curiosidad?
fuente