2010-01-08 22 views
71

¿Hay algún estilo de codificación recomendado para escribir scripts de PowerShell? Es no sobre cómo estructurar el código (cuántas funciones, si usar módulo, ...). Se trata de 'cómo escribir el código para que sea legible'.¿Cuál es el estilo de codificación recomendado para PowerShell?

En lenguajes de programación, son algunos recomienda codificación de estilos (qué guión, cómo se va a sangrar - espacios/pestañas, dónde hacen nueva línea, dónde colocar los apoyos , ...), pero no he visto ninguna sugerencia para PowerShell.

Lo que me interesa particularmente en:


cómo escribir parámetros

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

(veo que es más como la sintaxis 'V1') o

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

o (¿por qué agregar el atributo de vacío?)

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

o (otro formato vi tal vez en el código de Jaykul)

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

o ..?


cómo escribir tubería 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 lo que si hay -Comience params -END -Proceso? ¿Cómo hacer que sea 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 indentitation es importante aquí y qué elemento se pone en la nueva línea también.


He cubierto solo las preguntas que me vienen a la mente con mucha frecuencia. Hay algunos otros, pero me gustaría mantener esta pregunta tan 'corta'.

Cualquier otra sugerencia es bienvenida.

+1

Supongo que la falta de un estilo de codificación común para los scripts de PowerShell se debe al hecho de que está más relacionado con el uso del administrador en lugar de la codificación "real". – Filburt

+3

Tienes razón. Sin embargo, los administradores de imho necesitan scripts que sean fáciles de leer. Por ejemplo, no me gustan los backticks, así que trato de evitarlos. – stej

+2

Esta es una gran pregunta. –

Respuesta

77

Después de pasar un par de años de buceo muy profundamente en Powershell v2.0, aquí es lo que he decidido por:

<# 
.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 { 
     $_ 
    } 
} 

Vaya, que consiguió más de lo que esperaba.Espero que las respuestas que quieres estén ahí en alguna parte :)

editar - El resaltador de sintaxis de StackOverflow se está rindiendo completamente. Pégalo en el ISE.

+0

Gracias por la respuesta integral; Probablemente lo marque como una respuesta aceptada, parece que nadie más está interesado en el estilo de codificación Posh: | ¿Ha publicado en alguna parte su helper funciones (??,?:,? +, Im, ...)? - Sería valioso para muchas personas, creo;) – stej

+0

No, yo no ... Sí, debería ... ¡uno de estos días ...! –

+3

Ok, cometí v0.1 en algún lugar público. Vaya a http://tfstoys.codeplex.com/SourceControl/changeset/view/33350#605701 y vaya a Módulos \ RichardBerg-Misc –

7

Recientemente me encontré con an excellent point about indent style in PowerShell. Como los estados de comentarios enlazados, observar la diferencia entre estas mismas sintaxis:

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

y

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

Mientras mi inclinación es de "hacer lo que vieres" y utilizar el # estilo estándar C muesca (Allman , más o menos), estoy en desacuerdo con esta excepción y otras similares.

Esto me inclina personalmente a usar mi 1TBS favorito, pero podría convencerme de lo contrario. ¿Cómo te arreglaste, por curiosidad?

+2

Soy bastante nuevo en posh. ¡Gracias por el aviso! Al principio me gustaba la línea separada, pero ahora me gusta abrir curly en la línea de configuración. – AnneTheAgile

+0

Puede encontrar animosidad de los codificadores .NET que usan el estándar de C#, pero cuando los cambios de sangría funcionan, voy con lo que siempre hará lo que se espera sobre cualquier preferencia religiosa, en cualquier momento. Tiendo a preferir 1TBS para todo, pero si el ejemplo anterior muestra un comportamiento inverso, todo mi PoSh se utilizará en un abrir y cerrar de ojos. :) – Tohuw

+0

Cuidado, estás combinando Estilo con comportamiento. –

5

Para el registro, también hay The PowerShell Best Practices and Style Guide.

No es oficial, pero tiene varios puntos buenos.

+0

404: los enlaces están rotos. –

+0

Reparado. Esta nueva guía se creó fusionando la antigua * Guía de estilo de PowerShell *, de Carlos Pérez (que originalmente vinculé) y * The Community Book of PowerShell Practices *, de Don Jones y Matt Penny. – rsenna

+3

Esta respuesta realmente debería ser más alta ahora. –

Cuestiones relacionadas