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
또는 (새 줄의 cmdlet 이름)
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 모범 사례 및 스타일 가이드 라고 생각합니다 .
그들의 소개에서 :
영어 철자 및 문법 규칙과 마찬가지로 PowerShell 프로그래밍 모범 사례 및 스타일 규칙에는 거의 항상 예외가 있지만, 일반적인 문제를 피하고 도움을 줄 수있는 코드 구조, 명령 디자인, 프로그래밍, 서식 및 스타일에 대한 기준선을 문서화하고 있습니다. 재사용 가능하고 읽기 쉬운 코드를 작성할 수 있습니다. 재사용 가능한 코드를 다시 작성할 필요가없고 읽기 쉬운 코드를 유지할 수 있기 때문입니다.
또한 다음 GitBook 링크를 사용할 수 있도록했습니다.
답변
최근 에 PowerShell의 들여 쓰기 스타일에 대한 훌륭한 점을 발견 했습니다 . 링크 된 주석에서 언급했듯이 다음과 같은 동일한 구문의 차이점을 관찰하십시오.
1..10 | Sort-Object
{
-$_
}
과
1..10 | Sort-Object {
-$_
}
내 경향은 “로마인처럼 수행”하고 표준 C # 들여 쓰기 스타일 ( Allman , 다소간)을 ,이 예외 및 이와 유사한 다른 문제가 발생합니다.
이것은 개인적으로 내가 선호하는 1TBS 를 사용하는 경향 이 있지만 그렇지 않으면 확신 할 수 있습니다. 호기심 때문에 어떻게 정착 했습니까?