Каков рекомендуемый стиль кодирования для PowerShell?
есть ли рекомендуемый стиль кодирования, как писать скрипты PowerShell? Это не о том, как структурировать код (сколько функций, если использовать модуль, ...). Речь идет о'как написать код так, чтобы он читался'.
в языках программирования есть несколько рекомендуемые стили кодирования (что на абзац, как отступ-пробелы / вкладки, где сделать новая строка, где поставить брекеты,...), но я не видел никаких предложений для PowerShell.
что меня особенно интересует:
как писать параметры
function New-XYZItem
( [string] $ItemName
, [scriptblock] $definition
) { ...
(Я вижу, что это больше похоже на синтаксис 'V1)
или
function New-PSClass {
param([string] $ClassName
,[scriptblock] $definition
)...
или (зачем добавлять пустой атрибут?)
function New-PSClass {
param([Parameter()][string] $ClassName
,[Parameter()][scriptblock] $definition
)...
или (другое форматирование, которое я видел, возможно, в коде Джейкула)
function New-PSClass {
param(
[Parameter()]
[string]
$ClassName
,
[Parameter()]
[scriptblock]
$definition
)...
или ..?
How to написать сложный конвейер
Get-SomeData -param1 abc -param2 xyz | % {
$temp1 = $_
1..100 | % {
Process-somehow $temp1 $_
}
} | % {
Process-Again $_
} |
Sort-Object -desc
или (имя командлета в новой строке)
Get-SomeData -param1 abc -param2 xyz |
% {
$temp1 = $_
1..100 |
% {
Process-somehow $temp1 $_
}
} |
% {
Process-Again $_
} |
Sort-Object -desc |
а что, если есть-begin-process-end params? как сделать его максимально читаемым?
Get-SomeData -param1 abc -param2 xyz |
% -begin {
init
} -process {
Process-somehow2 ...
} -end {
Process-somehow3 ...
} |
% -begin {
} ....
или
Get-SomeData -param1 abc -param2 xyz |
% `
-begin {
init
} `
-process {
Process-somehow2 ...
} `
-end {
Process-somehow3 ...
} |
% -begin {
} ....
отступ здесь важен, и какой элемент ставится на новую строку.
я затронул только вопросы, которые приходят мне на ум очень часто. Есть и другие, но я хотел бы сохранить это так вопрос "короткий".
любые другие предложения приветствуются.
3 ответов:
проведя пару лет, погружаясь довольно глубоко в Powershell v2. 0, вот на чем я остановился:
<# .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 { $_ } }Ой, это было дольше, чем я ожидал. Надеюсь, что ответы, которые вы хотели, находятся где-то там :)
edit - синтаксический маркер StackOverflow полностью отказывается от меня. Вставьте его в ISE.
для записи, есть также рекомендации PowerShell и руководство по стилю.
Он далек от официального, но имеет несколько хороших моментов.
Я недавно наткнулся на отличный момент о стиле отступа в PowerShell. Как говорится в связанном комментарии, обратите внимание на разницу между этими же синтаксисами:
1..10 | Sort-Object { -$_ }и
1..10 | Sort-Object { -$_ }хотя я склоняюсь к тому, чтобы "делать как римляне" и использовать стандартный стиль отступа C# (Allman, более или менее), я беру проблему с этим исключением и другими подобными ему.
это склоняет меня лично использовать мой любимый 1ст. ложка, но я могу быть убежден в противном случае. Как вы устроились, из любопытства?
Comments