Есть ли рекомендуемый стиль написания сценариев 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
)...
или же ...?
Как написать сложный конвейер
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
параметры? Как сделать его максимально читаемым?
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 {
} ....
Здесь важен отступ и то, какой элемент ставится на новую строку.
Я ответил только на вопросы, которые приходят мне в голову очень часто. Есть и другие, но я бы хотел, чтобы этот вопрос о переполнении стека был «коротким».
Любые другие предложения приветствуются.
Ответы:
Потратив пару лет на глубокое погружение в PowerShell v2.0, я остановился на следующем:
<# .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 { $_ } }
Подсветка синтаксиса Stack Overflow полностью меня разочаровала. Вставьте его в ISE.
источник
Я считаю, что наиболее исчерпывающим ресурсом по стилю кодирования для PowerShell по-прежнему является PowerShell Best Practices and Style Guide .
Из их введения:
Они также сделали доступными эти ссылки на GitBook :
источник
Недавно я наткнулся на отличную мысль о стиле отступа в PowerShell . Как говорится в связанном комментарии, обратите внимание на разницу между этими синтаксисами:
1..10 | Sort-Object { -$_ }
и
1..10 | Sort-Object { -$_ }
Хотя я склонен «поступать как римляне» и использовать стандартный стиль отступов C # ( более или менее Allman ), я не согласен с этим исключением и другими похожими на него исключениями.
Это побуждает меня лично использовать мои любимые 1TBS , но меня можно было убедить в обратном. Как вы устроились из любопытства?
источник