PowerShellで推奨されるコーディングスタイルとは?
2023-07-16 10:03:40
質問
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 {
} ....
ここではインデントが重要で、どのような要素を改行するのかも重要です。
私がよく思い浮かべる質問だけを取り上げました。他にもいくつかありますが、この Stack Overflow の質問は「短く」したいと思います。
他のどんな提案も歓迎します。
どのように解決するのですか?
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にターミネーターがない。"
-
[解決済み] Get-ADUser -Identity
-
[解決済み] PowerShellにおけるセット(データ構造)
-
[解決済み] Write-ErrorとThrowはいつ使い分ける?終端エラーと非終端エラー
-
[解決済み] Javascriptのファイル全体を「(function(){ ... })()」のような無名関数で囲むのは何のためでしょうか?
-
[解決済み] 現在のPowerShellスクリプトの場所を特定する最良の方法は何ですか?
-
[解決済み] std::vectorのイテレータのインデックスを取得する最も効果的な方法は何ですか?
-
[解決済み] Windows PowerShellで現在のユーザー名を取得する方法を教えてください。
-
[解決済み】PowerShellでエコーと同等のスクリプトテストが可能
-
[解決済み】PowerShellの$_の意味は何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] PowerShellです。エラー "引数 ... を受け入れる位置パラメーターが見つかりません"
-
[解決済み] powershellでパラメータ'Path'がNULLのため引数を結合できないエラーの取得
-
[解決済み] Resolve-Pathコマンドレットは、具体的にどのような処理を行うのですか?
-
[解決済み] スクリプトへのパスが完全修飾されている場合、削除サーバーでのPowershellの実行に失敗する
-
[解決済み] AzureのAdd-AzureDiskが動作しない
-
[解決済み] Powershellでパラメータを切り替える
-
[解決済み] cmdletbinding()】とは何ですか、どのように機能するのですか?
-
[解決済み] PowerShellで使用するGetType、変数の違いについて
-
[解決済み] Powershell New-ScheduledTaskTrigger -RepetitionIntervalを使った反復タスクの実行
-
[解決済み] Install-Module : 'Install-Module' という用語は、コマンドレット名として認識されません。