他の多くの言語と同様に、PowerShell にはスクリプトでコードを条件付きで実行するためのステートメントがあります。 これらのステートメントの 1 つは If ステートメントです。 本日は、PowerShell の最も基本的なコマンドの 1 つについて詳しく説明します。
注
この記事の 元のバージョン は、 @KevinMarquetteによって書かれたブログに掲載されました。 PowerShell チームは、このコンテンツを Microsoft と共有してくれた Kevin に感謝します。 PowerShellExplained.com で彼のブログをチェックしてください。
条件付き実行
スクリプトでは、多くの場合、意思決定を行い、それらの決定に基づいて異なるロジックを実行する必要があります。
これは条件付き実行の意味です。 評価するステートメントまたは値が 1 つある場合は、その評価に基づいて別のコード セクションを実行します。 これは、 if ステートメントが行うとおりです。
if ステートメント
if ステートメントの基本的な例を次に示します。
$condition = $true
if ( $condition )
{
Write-Output "The condition was true"
}
ifステートメントで最初に行うことは、式をかっこで囲んで評価することです。
$trueと評価された場合、中かっこ内のステートメントが実行されます。 値が $falseされた場合は、そのステートメント ブロックをスキップします。
前の例では、 if ステートメントは、 $condition 変数を評価するだけです。 これは $true され、ステートメント ブロック内で Write-Output コマンドを実行した可能性があります。
一部の言語では、 if ステートメントの後に 1 行のコードを配置して実行できます。 PowerShell ではそうではありません。 正しく動作させるには、中かっこを含めた完全な statement block を指定する必要があります。
比較演算子
if ステートメントの最も一般的な用途は、2 つの項目を相互に比較することです。 PowerShell には、さまざまな比較シナリオ用の特殊な演算子があります。 比較演算子を使用すると、左側の値が右側の値と比較されます。
-eq for equality
-eqは、2 つの値が互いに等しいことを確認するために、2 つの値の間で等値チェックを行います。
$value = Get-MysteryValue
if ( 5 -eq $value )
{
# do something
}
この例では、 5 の既知の値を取得し、それを $value と比較して一致するかどうかを確認します。
考えられるユース ケースの 1 つは、アクションを実行する前に値の状態を確認することです。 サービスを取得し、Restart-Service を呼び出す前にステータスが稼働中であることを確認することができます。
C# などの他の言語では、等価性のために == を使用するのが一般的ですが (例: 5 == $value)、PowerShell では機能しません。 ユーザーが行うもう 1 つの一般的な間違いは、変数に値を割り当てるために予約されている等号 (例: 5 = $value) を使用することです。 既知の値を左に配置すると、その間違いをより厄介なものにします。
この演算子 (およびその他) には、いくつかのバリエーションがあります。
-
-eq大文字と小文字を区別しない等価性 -
-ieq大文字と小文字を区別しない等価性 -
-ceq大文字と小文字を区別する等価性
-ne は「等しくない」を意味する
多くの演算子には、逆の結果をチェックする関連演算子があります。
-ne は、値が互いに等しくなっていないかどうかを検証します。
if ( 5 -ne $value )
{
# do something
}
これを使用して、値が 5されていない場合にのみアクションが実行されるようにします。 サービスを開始しようとする前に、それが実行中の状態であるかどうかを確認するのは良い利用ケースです。
バリエーション:
-
-ne大文字と小文字を区別しない非等価 -
-ine大文字と小文字を区別しない不等式 -
-cne大文字と小文字を区別する不等号
これらは、 -eqの逆のバリエーションです。 他の演算子のバリエーションを一覧表示するときに、これらの型をグループ化します。
-gt -ge -lt -le より大きいか小さいか
これらの演算子は、値が別の値より大きいか小さいかを確認するときに使用されます。
-gt -ge -lt -leは、GreaterThan、GreaterThanOrEqual、LessThan、LessThanOrEqual を表します。
if ( $value -gt 5 )
{
# do something
}
バリエーション:
-
-gtより大きい -
-igtより大きい、大文字と小文字が区別されない -
-cgtより大きい、大文字と小文字が区別される -
-ge以上または等しい -
-ige大文字と小文字を区別せず、以上 -
-cge以上または等しい、大文字小文字を区別 -
-lt未満 -
-iltより小さい、大文字と小文字を区別しない -
-cltより小さい、ケースセンシティブ -
-le次の値以下 -
-ile以下、大文字と小文字を区別しない -
-cle以下、大文字と小文字が区別されます
これらの演算子に対して大文字小文字を区別するかどうか選べるオプションを使用する理由がわかりません。
-like形式のワイルドカード一致
PowerShell には独自のワイルドカード ベースのパターン マッチング構文があり、 -like 演算子で使用できます。 これらのワイルドカード パターンはかなり基本的です。
-
?任意の 1 文字に一致します -
*任意の数の文字に一致します
$value = 'S-ATX-SQL01'
if ( $value -like 'S-*-SQL??')
{
# do something
}
パターンが文字列全体と一致することを指摘することが重要です。 文字列の中央にある何かを一致させる必要がある場合は、文字列の両端に * を配置する必要があります。
$value = 'S-ATX-SQL02'
if ( $value -like '*SQL*')
{
# do something
}
バリエーション:
-
-like大文字と小文字を区別しないワイルドカード -
-ilike大文字と小文字を区別しないワイルドカード -
-clike大文字と小文字を区別するワイルドカード -
-notlike大文字と小文字を区別しないワイルドカードが一致しない -
-inotlike大文字と小文字を区別しないワイルドカードがマッチしない -
-cnotlike大文字と小文字を区別するワイルドカードが一致しない
-match 正規表現
-match演算子を使用すると、正規表現ベースの一致を文字列で確認できます。 ワイルドカード パターンの柔軟性が十分でない場合に使用します。
$value = 'S-ATX-SQL01'
if ( $value -match 'S-\w\w\w-SQL\d\d')
{
# do something
}
正規表現パターンは、既定で文字列内の任意の場所と一致します。 したがって、次のように一致させる部分文字列を指定できます。
$value = 'S-ATX-SQL01'
if ( $value -match 'SQL')
{
# do something
}
正規表現はそれ自体の複雑な言語であり、調べることに値します。 私は -match と別 の記事で正規表現を使用する多くの方法 について詳しく説明します。
バリエーション:
-
-match大文字と小文字を区別しない正規表現 -
-imatch大文字と小文字を区別しない正規表現 -
-cmatch大文字と小文字を区別する正規表現 -
-notmatch大文字と小文字を区別しない正規表現が一致しない -
-inotmatch大文字と小文字を区別しない正規表現が一致しない -
-cnotmatch大文字と小文字を区別する正規表現が一致しない
の型です
-is演算子を使用して、値の型を確認できます。
if ( $value -is [string] )
{
# do something
}
これは、クラスを操作している場合や、パイプラインでさまざまなオブジェクトを受け入れる場合に使用できます。 入力としてサービスまたはサービス名を指定できます。 次に、サービスがあるかどうかを確認し、名前しかない場合はサービスを取得します。
if ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
$Service = Get-Service -Name $Service
}
バリエーション:
-
-is型 -
-isnot型ではない
コレクション演算子
前の演算子を 1 つの値で使用すると、結果は $true または $falseになります。 これは、コレクションを操作する場合に若干異なる方法で処理されます。 コレクション内の各項目が評価され、 $trueに評価されるすべての値が返されます。
PS> 1,2,3,4 -eq 3
3
これは、 if ステートメントでも正しく機能します。 したがって、演算子によって値が返され、ステートメント全体が $trueされます。
$array = 1..6
if ( $array -gt 3 )
{
# do something
}
ここに私が指摘する必要がある細部に隠れている小さなトラップが1つあります。このように -ne 演算子を使用すると、誤ってロジックを後方に見やすくなります。 コレクションで -ne を使用すると、コレクション内の項目が値と一致しない場合に $true が返されます。
PS> 1,2,3 -ne 4
1
2
3
これは巧妙なトリックのように見えるかもしれませんが、これをより効率的に処理する演算子 -contains と -in があります。 そして、 -notcontains はあなたが期待したことを行います。
-含む
-contains演算子は、コレクションの値を確認します。 一致が見つかるとすぐに、 $trueが返されます。
$array = 1..6
if ( $array -contains 3 )
{
# do something
}
これは、コレクションに値が含まれているかどうかを確認するための推奨される方法です。
Where-Object (または-eq) を使用すると、毎回リスト全体が表示され、大幅に遅くなります。
バリエーション:
-
-contains大文字と小文字を区別しない一致 -
-icontains大文字と小文字を区別しない一致 -
-ccontains大文字と小文字を区別する一致 -
-notcontains大文字と小文字が区別されない -
-inotcontains大文字と小文字が区別されない -
-cnotcontains大文字と小文字が一致しない
-インチ
-in演算子は、コレクションが右側にある点を除き、-contains演算子と同じです。
$array = 1..6
if ( 3 -in $array )
{
# do something
}
バリエーション:
-
-in大文字と小文字を区別しない一致 -
-iin大文字と小文字を区別しない一致 -
-cin大文字と小文字を区別する一致 -
-notin大文字と小文字が区別されない -
-inotin大文字と小文字が区別されない -
-cnotin大文字と小文字が区別されない
論理演算子
論理演算子は、他の式を反転または結合するために使用されます。
-じゃない
-not演算子は、式を$falseから$trueに、または$trueから$falseに反転します。
Test-Pathが$falseされたときにアクションを実行する例を次に示します。
if ( -not ( Test-Path -Path $path ) )
ここで説明した演算子のほとんどは、 -not 演算子を使用する必要がないバリエーションがあります。 しかし、それが役に立つことはまだあります。
! オペレータ
!のエイリアスとして-notを使用できます。
if ( -not $value ){}
if ( !$value ){}
C# などの別の言語から来たユーザーによって使用される ! が増える場合があります。 私は、それを打ち込むことを好みます。なぜなら、私のスクリプトをぱっと見たときに見づらいためです。
-そして
式を -and 演算子と組み合わせることができます。 式全体を$trueにするためには、両方の側を$trueにする必要があります。
if ( ($age -gt 13) -and ($age -lt 55) )
この例では、 $age は左側が 13 以上、右側が 55 未満である必要があります。 その例でわかりやすくするためにかっこを追加しましたが、式が単純である限り省略可能です。 次に示すのは、これらの例を除いた例です。
if ( $age -gt 13 -and $age -lt 55 )
評価は左から右に行われます。 最初の項目が $falseと評価された場合、最初の項目は早期に終了し、適切な比較は実行されません。 これは、使用する前に値が存在することを確認する必要がある場合に便利です。 たとえば、Test-Pathパスを指定すると、$nullがエラーを投げます。
if ( $null -ne $path -and (Test-Path -Path $path) )
-または
-orを使用すると、2 つの式を指定でき、いずれかの式が$true場合は$trueを返します。
if ( $age -le 13 -or $age -ge 55 )
-and演算子と同様に、評価は左から右に行われます。 最初の部分が $true場合、ステートメント全体が $true され、式の残りの部分は処理されません。
また、これらの演算子の構文のしくみもメモしておきます。 2 つの個別の式が必要です。 私は、ユーザーが自分の間違いを認識することなく、このような $value -eq 5 -or 6 を試みているのを見てきました。
-xor exclusive または
これは少し珍しいです。
-xor では、 $trueに評価できる式は 1 つだけです。 したがって、両方の項目が $false されているか、両方の項目が $trueされている場合、式全体が $falseされます。 別の見方としては、式の結果が異なる場合にのみ、式が$trueです。
誰もがこの論理演算子を使用することはまれであり、なぜ私がそれを使用するのかについて良い例を考えることはできません。
ビットごとの演算子
ビットごとの演算子は、値内のビットに対して計算を実行し、結果として新しい値を生成します。 ビットごとの演算子の教えは、この記事の範囲外ですが、それらの一覧を次に示します。
-
-bandbinary AND -
-borbinary OR -
-bxorバイナリ排他 OR -
-bnotbinary NOT -
-shl左シフト -
-shr右にシフト
PowerShell 式
condition ステートメント内で通常の PowerShell を使用できます。
if ( Test-Path -Path $Path )
Test-Path は、実行時に $true または $false を返します。 これは、他の値を返すコマンドにも適用されます。
if ( Get-Process Notepad* )
返されたプロセスがある場合は $true に評価され、返されない場合は $false されます。 パイプライン式または次のような他の PowerShell ステートメントを使用することは完全に有効です。
if ( Get-Process | where Name -EQ Notepad )
これらの式は、 -and 演算子と -or 演算子と組み合わせることができますが、かっこを使用して部分式に分割する必要がある場合があります。
if ( (Get-Process) -and (Get-Service) )
$nullをチェックする
結果が存在しない場合や$nullの値がifステートメントで$falseと評価される場合があります。
$nullを確認する場合は、左側に$nullを配置することをお勧めします。
if ( $null -eq $value )
PowerShell で $null 値を扱うときには、かなりの違いがあります。 あなたがより深く飛び込むのに興味があるなら、私 はあなたが$nullについて知りたかったすべてについての記事を持っています。
条件内での変数の代入
プラスーン・カルナンVが私にそれを思い出させるまで、私はほとんどこれを追加するのを忘れていました。
if ($process=Get-Process notepad -ErrorAction Ignore) {$process} else {$false}
通常、変数に値を割り当てると、値はパイプラインまたはコンソールに渡されません。 サブ式で変数の代入を行うと、パイプラインに渡されます。
PS> $first = 1
PS> ($second = 2)
2
$first割り当てには出力がなく、$second割り当てには出力がありますか?
if ステートメントで割り当てが行われると、上記の$secondの割り当てと同じように実行されます。 これを使用する方法のクリーンな例を次に示します。
if ( $process = Get-Process Notepad* )
{
$process | Stop-Process
}
$processに値が割り当てられると、ステートメントは$trueされ、$processは停止します。
これは等値チェックではないので、これを -eq と混同しないようにしてください。 これは、ほとんどの人がこのように動作することを認識していない、より不明瞭な機能です。
ステートメント ブロックからの変数の割り当て
if ステートメント ステートメント ブロックを使用して、変数に値を割り当てることもできます。
$discount = if ( $age -ge 55 )
{
Get-SeniorDiscount
}
elseif ( $age -le 13 )
{
Get-ChildDiscount
}
else
{
0.00
}
各スクリプト ブロックは、コマンドの結果または値を出力として書き込みます。
ifステートメントの結果を$discount変数に割り当てることができます。 この例では、これらの値を各ステートメント ブロック内の $discount 変数に直接簡単に割り当てることができます。 私は頻繁に if ステートメントでこれを使用するとは言えませんが、最近これを使用した例があります。
代替実行パス
if ステートメントを使用すると、ステートメントが$trueされたときだけでなく、$falseの場合にもアクションを指定できます。 ここで、 else ステートメントが実行されます。
然も無くば
else ステートメントは、常にif ステートメントの最後の部分です。
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
Write-Warning "$path doesn't exist or isn't a file."
}
この例では、 $path がファイルであることを確認します。 ファイルが見つかると、ファイルが移動されます。 そうでない場合は、警告を書き込みます。 この種の分岐ロジックは非常に一般的です。
入れ子になった場合
ifステートメントと else ステートメントはスクリプト ブロックを受け取るので、別の if ステートメントを含め、その中に任意の PowerShell コマンドを配置できます。 これにより、はるかに複雑なロジックを使用できます。
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
else
{
if ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
}
この例では、最初に幸せなパスをテストしてから、それに対してアクションを実行します。 失敗した場合は、別のチェックを行い、ユーザーにより詳細な情報を提供します。
Elseif
単一の条件付きチェックに限定されるわけではありません。
if ステートメントを使用して入れ子にするのではなく、elseステートメントと elseif ステートメントを連結できます。
if ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
elseif ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
else
{
Write-Warning "$path could not be found."
}
実行は上から下に行われます。 先頭 if ステートメントが最初に評価されます。
$falseされている場合は、次のelseifまたはリスト内のelseに移動します。 その最後の else は、他の誰も $trueを返さない場合に実行する既定のアクションです。
スイッチ
この時点で、 switch ステートメントについて言及する必要があります。 これは、値と複数の比較を行うための代替構文を提供します。
switchでは、式を指定すると、その結果が複数の異なる値と比較されます。 これらの値のいずれかが一致する場合は、一致するコード ブロックが実行されます。 この例を見てみましょう。
$itemType = 'Role'
switch ( $itemType )
{
'Component'
{
'is a component'
}
'Role'
{
'is a role'
}
'Location'
{
'is a location'
}
}
$itemTypeと一致する可能性のある 3 つの値があります。 この場合、 Roleと一致します。 単純な例を用いて、switch 演算子に慣れていただくために使用しました。 別の記事の switch ステートメントについて知りたかったすべてのことについて 詳しく説明します。
配列インライン
いくつかのコマンドライン引数で実行可能ファイルを起動する Invoke-SnowSql という関数があります。 引数の配列を構築するその関数のクリップを次に示します。
$snowSqlParam = @(
'--accountname', $Endpoint
'--username', $Credential.UserName
'--option', 'exit_on_error=true'
'--option', 'output_format=csv'
'--option', 'friendly=false'
'--option', 'timing=false'
if ($Debug)
{
'--option', 'log_level=DEBUG'
}
if ($Path)
{
'--filename', $Path
}
else
{
'--query', $singleLineQuery
}
)
$Debug変数と$Path変数は、エンド ユーザーによって提供される関数のパラメーターです。
私は配列の初期化の中でそれらをインラインで評価します。
$Debugが true の場合、これらの値は正しい場所の$snowSqlParamに分類されます。
$Path変数についても同じことが当てはまります。
複雑な操作を簡略化する
チェックすべき比較が非常に多い状況に遭遇し、if ステートメントが画面の右端を越えてスクロールしてしまうのは避けられません。
$user = Get-ADUser -Identity $UserName
if ( $null -ne $user -and $user.Department -eq 'Finance' -and $user.Title -match 'Senior' -and $user.HomeDrive -notlike '\\server\*' )
{
# Do Something
}
読みにくい場合があり、間違いを犯しやすくなります。 それについてできることがいくつかあります。
行の継続
PowerShell には、コマンドを次の行にラップできる演算子がいくつかあります。 論理演算子 -and と -or は、式を複数行に分割する場合に使用するのに適した演算子です。
if ($null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
)
{
# Do Something
}
まだ多くのことが進行中ですが、それぞれの部分を独自の行に配置することで大きな違いが生まれます。 私は一般的に、2つ以上の比較を受け取るとき、またはロジックのいずれかを読み取るために右にスクロールする必要がある場合に使用します。
結果の事前計算
ifステートメントからそのステートメントを取り出し、結果のみを確認できます。
$needsSecureHomeDrive = $null -ne $user -and
$user.Department -eq 'Finance' -and
$user.Title -match 'Senior' -and
$user.HomeDrive -notlike '\\server\*'
if ( $needsSecureHomeDrive )
{
# Do Something
}
これは前の例よりもはるかにクリーンに感じます。 また、実際に確認している内容を説明する変数名を使用する機会もあります。 これは、不要なコメントを保存する自己文書化コードの例でもあります。
複数の if ステートメント
これを複数のステートメントに分割し、一度に 1 つずつチェックできます。 この場合、フラグまたは追跡変数を使用して結果を結合します。
$skipUser = $false
if( $null -eq $user )
{
$skipUser = $true
}
if( $user.Department -ne 'Finance' )
{
Write-Verbose "isn't in Finance department"
$skipUser = $true
}
if( $user.Title -match 'Senior' )
{
Write-Verbose "Doesn't have Senior title"
$skipUser = $true
}
if( $user.HomeDrive -like '\\server\*' )
{
Write-Verbose "Home drive already configured"
$skipUser = $true
}
if ( -not $skipUser )
{
# do something
}
フラグロジックを正しく動作させるためにロジックを反転する必要がありました。 各評価は、個々の if ステートメントです。 この利点は、デバッグ時にロジックの実行内容を正確に把握できることです。 私は同時にはるかに良い詳細性を追加することができました。
明らかな欠点は、記述するコードがはるかに多いということです。 コードは、1 行のロジックを受け取り、25 行以上に分解するため、見る方が複雑です。
関数の使用
また、その検証ロジックをすべて関数に移動することもできます。 全体の見た目が一目でクリーンだとわかります。
if ( Test-SecureDriveConfiguration -ADUser $user )
{
# do something
}
検証を行うには関数を作成する必要がありますが、このコードの操作がはるかに簡単になります。 これにより、このコードを簡単にテストできます。 テストでは、 Test-ADDriveConfiguration の呼び出しをモックすることができ、この関数に必要なテストは 2 つだけです。 1 つは $true を返し、1 つは $falseを返します。 他の関数のテストは非常に小さいため、より簡単です。
その関数の本体は、1行のコードである可能性もあれば、前のセクションで使用した詳細なロジックである可能性もあります。 これは両方のシナリオに適しており、後でその実装を簡単に変更できます。
エラー処理
if ステートメントの重要な用途の 1 つは、エラーが発生する前にエラー状態を確認することです。 適切な例は、フォルダーを作成する前に、フォルダーが既に存在するかどうかを確認する方法です。
if ( -not (Test-Path -Path $folder) )
{
New-Item -Type Directory -Path $folder
}
私はあなたが例外が起こることを期待しているならば、それは本当に例外ではないことを言いたいと思います。 そのため、値を確認し、可能な場所で条件を検証します。
実際の例外処理についてもう少し詳しく知りたい場合は、 例外について知りたいと思ったことすべてに関する記事があります。
最後の単語
ifステートメントは単純なステートメントですが、PowerShell の基本的な部分です。 これは、記述するほぼすべてのスクリプトで複数回使用していることがわかります。 私はあなたが前よりも良い理解を持っていることを願っています。
PowerShell