PowerShellについて調べてみた

本記事について

本記事は筆者が一部のコマンドをなんとなく使っていたPowerShellをきちんと活用できるために、PowerShellについて調べたものです。...正直、思ったよりできることが多くてびっくりしました。多すぎるので、他の言語と似ているところはある程度省略して、見ればなんとなく分かる程度にしました。でも多いです。

本記事はPowerShellの書き方をメインに扱っています。使えそうなコマンドは別の投稿にまとめました。

本記事のコマンドはPowerShell 7.4.6で動作確認しました。

はじめに

PowerShellとは

PowerShellは、コマンドライン シェル、スクリプト言語、および構成管理フレームワークで構成されるクロスプラットフォームのタスク自動化ソリューションです。テキストを受け入れて返すだけの大抵のシェルとは異なり、PowerShell は .NET オブジェクトを受け入れて返します。

https://learn.microsoft.com/ja-jp/powershell/scripting/overview?view=powershell-7.4

Windows Powershell

Windows 10や11に標準搭載されているPowerShellは「Windows PowerShell」といい、その名の通りWindows専用である。それとは別にLinux等でも動作するPowerShellがある。これらは似て非なるものであり、機能・コマンドなどが異なるところがある。詳細は公式ドキュメントに記事があるのでそちらを参照。

.NET

Windows PowerShellは.NET Framework、PowerShellは.NET Core上で動作しているという違いがあります。

コマンドレット

コマンドレットは PowerShell組み込みのコマンドで、「動詞-名詞」という名前規則になっている。たとえば Get-ChildItem というコマンドレットは 子アイテムを取得するコマンドである。

Get-ChildItem
(out)
(out)    Directory: D:\
(out)
(out)Mode                 LastWriteTime         Length Name
(out)----                 -------------         ------ ----
(out)d-r--          2024/11/13     9:26                desktop
(out)d-r--          2024/12/06    20:52                downloads
(out)d-r--          2024/11/20    19:56                files
(out)d-r--          2024/11/13     9:26                music
(out)d-r--          2024/11/13     9:26                pictures
(out)d----          2024/10/21    20:18                programs
(out)d-r--          2024/11/13     9:26                videos

パラメータ

コマンド名に続けて、そのコマンドの動作を制御する値をコマンドに渡すことができる。例えばGet-ChildItem というコマンドレットは、デフォルトでは現在位置の子アイテムを取得するが、Pathというパラメータに"C:\"という値を渡すとC:\の子アイテムを取得する。

Get-ChildItem -Path C:\
(out)
(out)    Directory: C:\
(out)
(out)Mode                 LastWriteTime         Length Name
(out)----                 -------------         ------ ----
(out)d----          2024/10/27    11:45                Brother
(out)d----          2022/06/29    22:54                drivers
(out)d----          2023/11/22    23:53                MSI
(out)d----          2024/04/01    16:26                PerfLogs
(out)d-r--          2024/11/15    19:00                Program Files
(out)d-r--          2024/11/13     9:15                Program Files (x86)
(out)d-r--          2024/11/13     9:15                Users
(out)d----          2024/11/25     0:34                Windows
(out)-a---          2024/02/22     1:33         112136 appverifUI.dll
(out)-a---          2024/02/22     1:34          66328 vfcompat.dll

パラメータは基本的には「-パラメータ名 パラメータの値」のような形で指定する。Get-ChildItemのPathパラメータのように、パラメータ名を省略できるものがある。以下の2つのコマンドは全く同じ意味である。

  • Get-ChildItem -Path C:\
  • Get-ChildItem C:\

また、パラメータの中にはON/OFFを切り替えるものがあり、スイッチパラメータと呼ぶ。スイッチパラメータは設定値を持たない。Get-ChildItemではファイルだけを表示するFileパラメータがあり、Fileパラメータが指定されていればON、指定されていなければOFFという意味になる。

  • Get-ChildItem # ファイルだけ表示:OFF
  • Get-ChildItem -File # ファイルだけ表示:ON

パラメータの終了

--は後に続く文字がパラメータで無いことを示す、別の言い方をすればパラメータが終了したことを表すトークンである。

-InputObjectという文字を出力したいとする。しかし、-InputObjectは出力コマンドwrite-outputのパラメータ名と重複しているため、そのままではパラメータとして解釈されてしまう。

write-output -InputObject
(out)Write-Output: Missing an argument for parameter 'InputObject'. Specify a parameter of type 'System.Management.Automation.PSObject' and try again.

これを解決するためには引用符でくくり文字列リテラルにするか、--を付けてパラメータとして解釈されないようにする。

write-output "-InputObject"
(out)-InputObject
(out)
write-output -- -InputObject
(out)-InputObject

エイリアス

コマンドに別の名前を与える。Get-Aliasを実行するとエイリアスの一覧を取得できる。その中には ls -> Get-ChildItem というものがあり、Get-ChildItem は ls というコマンドでも実行できる。

ls
(out)
(out)    Directory: D:\
(out)
(out)Mode                 LastWriteTime         Length Name
(out)----                 -------------         ------ ----
(out)d-r--          2024/11/13     9:26                desktop
(out)d-r--          2024/12/06    20:52                downloads
(out)d-r--          2024/11/20    19:56                files
(out)d-r--          2024/11/13     9:26                music
(out)d-r--          2024/11/13     9:26                pictures
(out)d----          2024/10/21    20:18                programs
(out)d-r--          2024/11/13     9:26                videos

Set-Aliasコマンドで、独自のエイリアスを登録することもできる。

ヘルプ

パラメータを含むコマンドの使い方を確認するには Get-Help コマンドを使う。

Get-Help Get-ChildItem
(out)
(out)NAME
(out)    Get-ChildItem
(out)
(out)SYNTAX
(out)    Get-ChildItem [[-Path] ] [[-Filter] ] [-Include ] [-Exclude ] [-Recurse] [-Depth ] [-Force] [-Name] [-Attributes {None | ReadOnly | Hidden | System | Directory | Archive | Device | Normal | Temporary | SparseFile | ReparsePoint | Compressed | Offline | NotContentIndexed | Encrypted | IntegrityStream | NoScrubData}] [-FollowSymlink] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] []
(out)
(out)ALIASES
(out)    gci
(out)    ls
(out)    dir
(out)
(out)(省略)

パイプライン

コマンドの実行結果を別のコマンドへ渡すことができる。command-1 | command-2 | command-3のようにパイプ|でつなげて記述する。

オブジェクト

コマンドの実行結果はオブジェクトとして出力される。オブジェクトはメンバーを持ち、メンバーに情報が格納されている。オブジェクトの種類はコマンドによって異なる。

オブジェクトが持っているメンバーを取得できるコマンドGet-Memberがある。Get-ChildItemの出力をGet-Memberに渡した結果を以下に示す。 この結果は、Get-ChildItemがSystem.IO.DirectoryInfo と System.IO.FileInfo の2種類のオブジェクトを渡したことと、それぞれのオブジェクトタイプが多数のメンバーを持つことが分かる。

Get-ChildItem | Get-Member
(out)
(out)   TypeName: System.IO.DirectoryInfo
(out)
(out)Name                      MemberType     Definition
(out)----                      ----------     ----------
(out)Target                    AliasProperty  Target = LinkTarget
(out)LinkType                  CodeProperty   System.String LinkType{get=GetLinkType;}
(out)...(中略)...
(out)
(out)
(out)   TypeName: System.IO.FileInfo
(out)
(out)Name                      MemberType     Definition
(out)----                      ----------     ----------
(out)Target                    AliasProperty  Target = LinkTarget
(out)LinkType                  CodeProperty   System.String LinkType{get=GetLinkType;}
(out)...(省略)...

メンバータイプ

メンバーには種類がある。主なものを示す。

AliasProperty他のメンバーの別名
NoteProperty静的な値を持つメンバー
ScriptPropertyPowerShellスクリプトを実行するプロパティ型のメンバー
Getter、Setterを定義できる
ScriptMethodPowerShellスクリプトを実行するメソッド型のメンバー
CodePropertyC#等の.NET言語で書かれたスクリプトを実行するプロパティ型のメンバー
Getter、Setterを定義
CodeMethodC#等の.NET言語で書かれたスクリプトを実行するメソッド型のメンバー

オブジェクトの表示

Get-ChildItemコマンドはDirectoryInfoやFileInfoオブジェクトを出力する。コンソール上にはオブジェクトが表形式で表示される。

Get-ChildItem
(out)
(out)    Directory: D:\
(out)
(out)Mode                 LastWriteTime         Length Name
(out)----                 -------------         ------ ----
(out)d-r--          2024/11/13     9:26                desktop
(out)d-r--          2024/12/06    20:52                downloads
(out)d-r--          2024/11/20    19:56                files
(out)d-r--          2024/11/13     9:26                music
(out)d-r--          2024/11/13     9:26                pictures
(out)d----          2024/10/21    20:18                programs
(out)d-r--          2024/11/13     9:26                videos

コマンドが最後まで実行され出力オブジェクトを渡すコマンドがなくなると、自動でOut-Defaultコマンドレットへ渡される。このOut-Defaultは一般ユーザが使う意味は無いが付けることもできる。

Get-ChildItem | Out-Default
(out)
(out)    Directory: D:\
(out)
(out)Mode                 LastWriteTime         Length Name
(out)----                 -------------         ------ ----
(out)d-r--          2024/11/13     9:26                desktop
(out)d-r--          2024/12/06    20:52                downloads
(out)d-r--          2024/11/20    19:56                files
(out)d-r--          2024/11/13     9:26                music
(out)d-r--          2024/11/13     9:26                pictures
(out)d----          2024/10/21    20:18                programs
(out)d-r--          2024/11/13     9:26                videos

Out-Defaultはオブジェクトに応じてどのように出力するかをバックグラウンドで決定する。もし、オブジェクトが文字列なら、単にOut-Hostを呼び出す。そうでなければ、オブジェクトの種類を調べ、このオブジェクトの種類に対応するデフォルトの表示形式が登録されているかどうかを調べる。もし、見つかった表示形式がテーブルであれば、Out-DefaultはFormat-Table | Out-Hostを呼び出す。

ビューとフォーマットデータ

表示形式の定義をビューと呼び、オブジェクトをどのようなビューで表示するかという設定をフォーマットデータと呼ぶ。Get-FormatData、Export-FormatData、Update-FormatDataなどにより確認や変更ができる。

FileInfoオブジェクトのビュー定義を取得してみる。

Get-FormatData System.IO.FileInfo
(out)
(out)TypeNames                                     FormatViewDefinition
(out)---------                                     --------------------
(out){System.IO.DirectoryInfo, System.IO.FileInfo} {children, childrenWithHardlink, children, children}

ここで表示されているFormatViewDefinitionはビューの定義オブジェクトである。FormatViewDefinitionを見れば、表形式かリスト形式かは分かる。

Get-FormatData System.IO.FileInfo | select -ExpandProperty FormatViewDefinition
(out)
(out)Name                 Control
(out)----                 -------
(out)children             System.Management.Automation.TableControl
(out)childrenWithHardlink System.Management.Automation.TableControl
(out)children             System.Management.Automation.ListControl
(out)children             System.Management.Automation.WideControl

実際にフォーマットデータをエクスポートしてみる。

Get-FormatData System.IO.FileInfo | Export-FormatData -Path testformat.xml


  
    
      children
      
        System.IO.DirectoryInfo
      
      
        PSParentPath
      
      
        
          
            
            7
            Left
          
          
            
            26
            Right
          
          
            
            14
            Right
          
          
            
            Left
          
        
        
          
            
            
              
                ModeWithoutHardLink
              
              
                LastWriteTimeString
              
              
                LengthString
              
              
                NameString
              
            
          
        
      
    
    (残りの3つのViewについては省略)

これを見ると、childrenというビューは見出しがMode、LastWriteTime、Length、Nameのテーブルがビューとして定義されている。それでは、これを踏まえてFileInfoを出力するGet-ChildItemの出力を見てみると、childrenビューと一致していることがわかる。

ビューの指定

デフォルトの表示形式に見たいメンバーが含まれていなかったり、違う形式で見たいことがある。そのような場合は表示形式を指定するformat-*コマンドが各種用意されている。

  • Format-Custom
  • Format-Hex
  • Format-List
  • Format-Table
  • Format-Wide

オブジェクトの文字列化

出力がオブジェクトであることが望ましくない場合もある。そのような場合、Out-Stringコマンドレットで出力を文字列として得ることができる。

単なる文字列となることで、見出しの色分けなどがされなくなっている。

変数

PowerShellの変数には数値や文字列、変数はドル記号$で始める。

$a = 10
$b = 20
$c = "text"
$a + $b
(out)30

コマンドの出力オブジェクトも格納できる。Get-ChildItemの実行結果を$childitem変数へ格納する例を示す。

$childitem = Get-ChildItem

変数はオブジェクトの型に縛られない。そのため、数値を入れた変数に文字列を入れることもできる。

$a = 10
$a = "text"

定数

Set-Variableコマンドレットを使用すると定数も作成できる。

Set-Variable a 10 -Option Constant
$a = 20
(out)WriteError: Cannot overwrite variable a because it is read-only or constant.

メンバー

変数がオブジェクトでメンバーを持つ場合、他の言語でも良くあるようにピリオドでメンバへアクセスできる。

$childitem = Get-ChildItem
$childitem.Name
(out)desktop
(out)downloads
(out)files
(out)music
(out)pictures
(out)programs
(out)videos

変数の種類

変数には3種類ある。

ユーザー作成変数ユーザーによって作成および管理される変数。既定ではPowerShell ウィンドウが開いている間のみ存在するが、 PowerShell プロファイルに追加すれば保存できる。
自動変数PowerShell が作成・管理する変数。 ユーザは参照のみできる。
ユーザ設定変数PowerShell のユーザー設定が格納される変数。 これらの変数は PowerShell によって作成され、既定値が設定される。 ユーザーはこれらの変数の値を変更して挙動を調整できる。

変数名に使用できる文字

変数には様々な文字が使用できるがベストプラクティスとしては英数字とアンダースコア (_)だけを使う。

$var = "text"
$var_1 = "text"

その他の文字を含める場合は{}で囲む。

# 例えば ハイフン - はそのままでは使えない。
$var-1 = "text"
(out)ParserError:
(out)Line |
(out)   1 |  $var-1 = "text"
(out)     |  ~~~~~~
(out)     | The assignment expression is not valid. The input to an assignment operator must be an object that is able to
(out)     | accept assignments, such as a variable or a property.
(out)
${var-1} = "text"
${変数}  = "text"

なお、変数名に閉じ中括弧}とバッククォート`は使用できない。

コレクション

配列

PowerShellでは@()で配列を表す。複数行で定義することもでき、その場合はカンマを省略できる。

$array = @('a','b','c')
(out)
$array1 = @(
(con)    'a'
(con)    'b'
(con)    'c'
(con))

また、コンマ区切りリストも使用できる。

$arrray2 = 'a','b','c'

指定番目の要素を取り出すには[]を使う。PowerShellは0ベースのインデックスである。

$array = @('a','b','c')
$array[0]
(out)a
特殊なインデックス指定

スライスのような記法や、負数インデックスもサポートされる。また、カンマで区切って複数のインデックスをまとめて指定できる。

$narray = 0,1,2,3,4,5,6,7,8,9
$narray[3..5]
(out)3
(out)4
(out)5
$narray[-1]
(out)9
$narray[3..-2]
(out)3
(out)2
(out)1
(out)0
(out)9
(out)8
$narray[1,5,7,8]
(out)1
(out)5
(out)7
(out)8
範囲外のインデックス

PowerShellでは範囲外のインデックスにアクセスしてもエラーにはならない。単にnullが返される。

$narray = 0,1,2,3,4,5,6,7,8,9
$narray[100]
$narray[100] -eq $null
(out)True

ハッシュテーブル

ハッシュテーブルはキーバリュー型のデータ構造。配列とは違い中括弧@{}を、区切りには;を使う。

$ht = @{ Path="downloads"; Filter="*.png" }
(out)
$ht = @{
(con)    Path="downloads"
(con)    Filter="*.png"
(con)}
(out)
$ht["Path"]
(out)downloads
$ht["Filter"]
(out)*.png
$ht["NoKey"]
$ht["NoKey"] -eq $null
(out)True

配列と同様に、存在しないキーにアクセスしてもエラーとならない。

スプラッティング

コマンドレットの引数を配列またはハッシュテーブルでまとめて指定できる。これをスプラッティングと呼び@演算子を使用する。スプラッティングを使うとパラメータが多く長くなってしまうコマンドを見やすく書くことができる。

例えば、Get-ChildItem -Path downloads -Filter *.pngというコマンドはスプラッティングを使うと以下のように書ける。プロパティ名と値をもつ場合はハッシュテーブルで、値だけをもつ場合は配列を使う。

$ht = @{
(con)    Path="downloads"
(con)    Filter="*.png"
(con)}
(out)
Get-ChildItem @ht

スイッチパラメータ

スイッチパラメータでスプラッティングを使うときは$trueをスイッチパラメータの値として渡す。

$ht = @{
(con)    Path="downloads"
(con)    Filter="*.png"
(con)    Directory=$true
(con)}
Get-ChildItem @ht

文字列

文字列は引用符で囲む。一重、二重のどちらでも使用できるが、二重引用符では変数が展開される、エスケープシーケンスが解釈されるという違いがある。

$var1 = "text"
$var2 = 'text'
$var1 -eq $var2
(out)True
'$var1'
(out)$var1
"$var2"
(out)text

二重引用符の文字列で、変数名の後に英数字または_が続く場合は、{}でどこまでが変数名かを示す。ミスを防ぐためには文字列の中では変数名は{}で囲む方が良いだろう。

$noun = "pen"
"I have a $noun."
(out)I have a pen.
"I have two $nouns."
(out)I have two .
"I have two ${noun}s."
(out)I have two pens.

Subexpression演算子

二重引用符の文字列で、変数そのものでは無く、式やコマンドの実行結果を展開したいときは$()で囲む。$()をSubexpression演算子とよび、ある式の中に別の式やコマンドを埋め込むものである。

ヒア文字列リテラル

@''@でまたは@""@で囲むとヒア文字列リテラルになる。複数行にまたがる文字列を表すことができる。一重と二重の違いは変数が展開されるかどうかで普通の文字列と同じである。

$here_string = @'
(out)  ∧ ∧
(out)/(´Д`)ノ
(out) ̄ ̄ ̄ ̄|
(out)'@
(out)
echo $here_string
(out)  ∧ ∧
(out)/(´Д`)ノ
(out) ̄ ̄ ̄ ̄|

エスケープシーケンス

二重引用符で囲まれた文字では特殊文字シーケンスがサポートされる。PowerShellではバッククォート(`)がエスケープ文字であり、改行であれば"`n"と書く。

`0Null
`aアラート
`bバックスペース
`eエスケープ(PowerShell6以降、主にテキスト装飾など)
`fフォームフィード
`n改行
`r復帰(キャリッジリターン)
`tタブ
`u{x}任意のUnicode文字
`v垂直タブ
`"二重引用符

文字列の結合

文字列を結合するときは+演算子や結合演算子-joinを使う。

"text" + "text"
(out)texttext
"text" + 0
(out)text0
-join ("text", "text")
(out)texttext
-join (0, "text")
(out)0text

数値など文字列以外と結合する場合、+演算子では計算順序によってエラーとなる。以下の例では"text"を数値へ変換しようとしてエラーとなる。

0 + "text"
(out)InvalidArgument: Cannot convert value "text" to type "System.Int32". Error: "The input string 'text' was not in a correct format."

区切り文字付きの結合

結合演算子-Joinは区切り文字付きの結合もサポートする。

#区切り文字なし
-Join (結合する文字1, ..., 結合する文字N)

#区切り文字有り
(結合する文字1, ..., 結合する文字N) -Join 区切り文字

StringBuilder

文字列を作成するSystem.Text.StringBuilderクラスも利用できる。大量の文字列操作を行う場合は、PowerShellでの操作よりも.NETの機能を利用したStringBuilderの方が高速な可能性がある。

参考:https://zenn.dev/haretokidoki/articles/bfc40f861a480d

Imvoke-Expression

Invoke-Expressionを使うと、文字列をコマンドとして実行できる。

構文

コメント

# 行コメント

<# ブロックコメント #>

コメントベースヘルプ

指定のフォーマットでコメントを書くと、コメントを元にヘルプテキストを生成してくれる。

<#
.

#>

関数のヘルプコメントなら以下のように書く。コメント位置はこの例で使っているfunction定義の前の他に、functionブロックの先頭か末尾に書いても良い。こうするとGet-Helpでコメントの内容がヘルプとして表示される。

<#
.SYNOPSIS
概要:○○する関数

.DESCRIPTION
詳細説明:○○して××する関数

.PARAMETER a
文字列A

.PARAMETER b
文字列B

.EXAMPLE
PS> Test-CommentBasedHelp -a "A" -b "B"

AとBを結合する。

.EXAMPLE
PS> Test-CommentBasedHelp -a "TEST"

TESTとBを結合する。

.NOTES
○○に注意

#>
function Test-CommentBasedHelp
{
    param($a="A", $b="B")

    ($a, $b) -join ' '
}

この関数を定義した後に、Get-Helpしたときの表示例を示す。このような表示されない場合は必須のキーワードが足りていないか、タイプミス等の可能性がある。

Get-Help Test-CommentBasedHelp
(out)
(out)名前
(out)    Test-CommentBasedHelp
(out)    
(out)概要
(out)    概要:○○する関数
(out)    
(out)    
(out)構文
(out)    Test-CommentBasedHelp [[-a] ] [[-b] ] []
(out)    
(out)    
(out)説明
(out)    詳細説明:○○して××する関数
(out)    
(out)
(out)関連するリンク
(out)
(out)注釈
(out)    例を参照するには、次のように入力してください: "get-help Test-CommentBasedHelp -examples".
(out)    詳細を参照するには、次のように入力してください: "get-help Test-CommentBasedHelp -detailed".
(out)    技術情報を参照するには、次のように入力してください: "get-help Test-CommentBasedHelp -full".
(out)
(out)
(out)
(out)PS> Get-Help Test-CommentBasedHelp -Example
(out)
(out)名前
(out)    Test-CommentBasedHelp
(out)    
(out)概要
(out)    概要:○○する関数
(out)    
(out)    
(out)    -------------------------- 例 1 --------------------------
(out)    
(out)    PS>Test-CommentBasedHelp -a "A" -b "B"
(out)    
(out)    AとBを結合する。
(out)    
(out)    
(out)    
(out)    
(out)    -------------------------- 例 2 --------------------------
(out)    
(out)    PS>Test-CommentBasedHelp -a "TEST"
(out)
(out)    TESTとBを結合する。
(out)
(out)
(out)
(out)
(out)




条件分岐

if-elseif-else

if ( condition1 ){
    condition1がtrueのときの処理
} elseif( condition2 ) {
    condition1がfalse かつ condition2がtrueのとき
} elseif ...
} else {
    すべてのconditionがfalseのときの処理
}

三項演算子

PowerShell 7.0で導入。

condition ? trueのときの式 : falseのときの式

switch

switch ( 評価式 )
{
    値1 { 評価式が値1のときの処理 }
    値2 { 評価式が値2のときの処理 }
    default { どの値にも一致しなかったときの処理 }
}

デフォルトで大文字小文字が区別されないので注意。

オプションでワイルドカードや正規表現による一致判定、大文字小文字を区別する比較も出来る。

switch [-regex | -wildcard] [-casesensitive] ( 評価式 )
{
    値1 { 評価式が値1のときの処理 }
    値2 { 評価式が値2のときの処理 }
}

$test="100-1000"
switch -regex ($test){
(con)    "〒?\d{7}"       { write-host "case1" }
(con)    "〒?\d{3}-\d{4}" { write-host "case2" }
(con)    default          { write-host "Not Match" }
(con)}
(out)case2

反復処理

for

for($i = 0; $i -lt 5; $i++){
(con)    write-host $i
(con)}
(out)0
(out)1
(out)2
(out)3
(out)4

foreach

$value = 0..4
foreach($i in $value){
(con)    write-host $i
(con)}
(out)0
(out)1
(out)2
(out)3
(out)4

パイプラインで渡されたオブジェクトに対して反復処理するときは以下のように書き、$_という変数に自動で格納される。

ls | foreach{ write-host $_.Name }
(out)desktop
(out)downloads
(out)files
(out)music
(out)pictures
(out)programs
(out)videos

エイリアス%も利用できる。ls | foreach{ write-host $_.Name }ls | %{ write-host $_.Name } は同じ意味である。

ForEach-Object

foreachはForEach-Objectコマンドレットのエイリアスである。標準的な使い方は上記の通りだが、詳細はForEach-Objectコマンドレットのヘルプを参照すること。

例として以下のような書き方もできる。

ls | foreach -begin {write-host "!!start!!"} -end {write-host "!!!end!!!"} {write-host $_.Name}
(out)
(out)!!start!!
(out)desktop
(out)downloads
(out)files
(out)music
(out)pictures
(out)programs
(out)videos
(out)!!!end!!!

do-while

$i = 0
do {
(con)    write-host $i
(con)}while( ++$i -lt 5 )
(out)0
(out)1
(out)2
(out)3
(out)4

do-until

$i = 0
do {
(con)    write-host $i
(con)}until( ++$i -eq 5 )
(out)0
(out)1
(out)2
(out)3
(out)4

while

while( $i -lt 5 ){
(con)    write-host ($i++)
(con)}
(out)0
(out)1
(out)2
(out)3
(out)4

break

直近の反復処理を抜ける。

continue

直近の反復処理で、次の処理に移る。

論理演算子

論理積(かつ)A -and B
論理和(または)A -or B
排他的論理和(どちらか一方)A -xor B
論理否定(でない)-not A または !A

比較演算子

大小関係

等しい(Equal)-eq
等しくない(Not Equal)-ne
より大きい(Greater Than)-gt
以上(Greater than or Equal)-ge
より小さい(Less Than)-lt
以下(Less than or Equal)-le

これらは文字列の大小関係を比較するときに、大文字と小文字を区別しない。-ceqのようにcを付けると大文字・小文字を区別する(Case sensitive)比較を行う。-ieqのようにiを付けると大文字・小文字を区別しない(case Insensitive)比較を行う。

文字列検査

ワイルドカードに一致する-like
ワイルドカードに一致しない-notlike
正規表現に一致する-match
正規表現に一致しない-notmatch

これらはいずれもc/iを付けることで大文字・小文字の区別の有無を指定できる演算子である。

$str = "122333444455555"
$str -like "1*5"
(out)True
$str -match "1{1}2{2}3{3}4{4}5{5}"
(out)True

コレクション検査

コレクションに含む-contains
コレクションに含まない-notcontains
コレクション内にある-in
コレクション内にない-notin

-contains/-notcontainsはc/iを付けることで大文字・小文字の区別の有無を指定できる演算子である。

$cl0 = 0..9
(out)
$cl0 -contains 4
(out)True
(out)
12 -notin $cl0
(out)True

型検査

型が同じ-is
型が異なる-isnot

コレクションの比較

大小関係を比較する演算子や文字列を検査する演算子は、スカラーかコレクションかで動作が異なる。

左辺がスカラーの場合、比較演算子は比較結果をTrueまたはFalseで返す。

(4 -eq 4)
(out)True

左辺がコレクションの場合、比較演算子はTrueとなる要素だけを残したコレクションを返す。

((1,2,3,4,5) -eq 4)
(out)4

オブジェクトの比較

等値性の比較インタフェース(IEquatable)

オブジェクトの等価性は既定ではオブジェクトのインスタンスが同じかどうかが検査される。System.IEquatable[Object]が実装されているオブジェクトではEqualsメソッドの定義に従う。

文字列Nameだけを持つ2種類のオブジェクトNameObject(Equalsメソッドを持たない)とEquatableNameObject(Equalsメソッドを持つ)で-eq演算子の違いについて確認する。

class NameObject {
(con)    [String]$Name
(con)}
(out)
class EquatableNameObject : System.IEquatable[Object] {
(con)    [String]$Name
(con)
(con)    [bool] Equals([Object] $obj) {
(con)        return $this.Name -ceq $obj.Name
(con)    }
(con)}
(out)
$a = [NameObject]@{Name = "text"}
$b = [NameObject]@{Name = "text"}
$a -eq $b
(out)False    # Nameの値は一緒だが、別のオブジェクトなのでFalse
(out)
$a = [EquatableNameObject]@{Name = "text"}
$b = [EquatableNameObject]@{Name = "text"}
$a -eq $b
(out)True     # Nameの値が一緒なので、別のオブジェクトでもTrue

NameObjectではNameの値が一緒でも結果がFalseとなっていることが確認できる。

大小関係の比較インタフェース(IComaparable)

オブジェクトの大小関係はSystem.IComparableを実装している場合のみ比較できる。

class NameObject {
(con)    [String]$Name
(con)}
(out)
class ComparableNameObject : System.IComparable {
(con)    [String]$Name
(con)
(con)    [int] CompareTo([Object] $obj) {
(con)        return $this.Name.CompareTo($obj.Name)
(con)    }
(con)}
(out)
$a = [NameObject]@{Name = "text1"}
$b = [NameObject]@{Name = "text0"}
$a -gt $b
(out)InvalidOperation: Cannot compare "NameObject" because it is not IComparable.
(out)
$a = [ComparableNameObject]@{Name = "text1"}
$b = [ComparableNameObject]@{Name = "text0"}
$a -gt $b
(out)True

例外処理

Get-ChildItemで存在しないパスを指定するとエラーメッセージが表示される。

Get-ChildItem -Path H:\
(out)Get-ChildItem: Cannot find drive. A drive with the name 'H' does not exist.

エラーが発生したときの処理はErrorActionで指定できる。主なものを示す。

Continueエラーメッセージを表示した上で処理を続行する。デフォルト値
Ignoreエラーを破棄し、コマンドの実行を続行する。
SilentlyContinueエラーメッセージを表示せず、コマンドの実行を続行する。エラーの情報は$Error自動変数に格納されている。
Stopエラーメッセージを表示せず、コマンドの実行を停止する。

try-catch

他の言語と同じようにtry...catch...finalyが利用できる。

try {
    $obj = Get-ChildItem -Path H:\

    write-host "H:\"
    foreach($item in $obj){
        write-host $item.Name
    }
}catch{
    write-host "パスが見つかりません。"
}

これを実行すると以下のようになり、例外がcatchされないことがわかる。

Get-ChildItem:
Line |
   2 |      $obj = Get-ChildItem -Path H:\
     |             ~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot find drive. A drive with the name 'H' does not exist.
H:\

これはエラーには処理を停止する「終了エラー」と、処理を続行する「終了しないエラー」があり、try...catchの対象となるのは終了エラーのみだからである。ErrorActionでStopを指定すると終了エラーとなり、想定通り動作するようになる。

try {
(con)    $obj = Get-ChildItem -Path H:\ -ErrorAction Stop
(con)
(con)    write-host "H:\"
(con)    foreach($item in $obj){
(con)        write-host $item.Name
(con)    }
(con)}catch{
(con)    write-host "パスが見つかりません。"
(con)}
(out)パスが見つかりません。

throw

throwで例外を投げることができる。throwは終了エラーである。

write-error

write-errorはエラーメッセージを表示する。write-errorは既定では終了しないエラーである。

型指定付きの例外処理

try
{
    throw [System.IO.FileNotFoundException] "Could not find: $path"
}
catch [System.IO.FileNotFoundException]
{
    ...
}
catch [Exception10], [Exception11], ...
{
    ...
}

関数

functionで関数を定義できる。

function 関数名
{
    処理内容
}

パラメーター

名前付きパラメーター

名前付き引数はparamで定義できる。

function 関数名
{
    param(引数の定義)

    処理内容
}

以下に3つの名前付きパラメータを持つ関数の例を示す。

function show3
{
    param($a, $b, $c)

    write-host "a=$a, b=$b, c=$c"
}

paramの代替構文

他の言語のような構文も使用できる。

function 関数名(引数の定義)
{
    処理内容
}

パラメータの値は渡すときは、定義した名前の後に値を書くか、定義した順序に従って値を渡す。以下は全て同じ結果になる。

show3 1 2 3
show3 -c 3 1 2
show3 -c 3 -b 2 -a 1

実引数の括弧

他の言語のように実引数を括弧で囲んだり、カンマを付けてはいけない。括弧を付けると一つ目の引数に配列が指定されたと見なされてしまう。

show3 1 2 3
(out)a=1, b=2, c=3
(out)
show3 1,2,3
(out)a=1 2 3, b=, c=
(out)
show3(1,2,3)
(out)a=1 2 3, b=, c=
位置指定パラメーター

名前を持たず、位置で指定するパラメータを使用できる。自動変数argsに格納されるため、argsを参照する。

function show3
(con){
(con)    write-host "0=$($args[0]), 1=$($args[1]), 2=$($args[2])"
(con)}
(out)
show3 "a" "b" "c"
(out)0=a, 1=b, 2=c

名前付きと位置指定の併用

名前付きパラメータと位置指定パラメータを同時に名前を指定しないで値を渡した場合、名前付きパラメータが先にあるものと見なされる。

function show3
(con){
(con)    param($a)
(con)    write-host "a=$a 0=$($args[0]), 1=$($args[1])"
(con)}
(out)
show3 "a" "b" "c"
(out)a=a 0=b, 1=c
スイッチパラメータ

スイッチパラメータを作成するには[switch]を付与する。

function switchparameter
(con){
(con)    param([switch] $detail)
(con)
(con)    "detail is $($detail ? 'on' : 'off')"
(con)}
(out)
switchparameter
(out)detail is off.
(out)
switchparameter -detail
(out)detail is on.
デフォルト値を持つパラメータ

パラメータ名の定義に続けて「=デフォルト値」と書く。

function defaulttest
(con){
(con)    param(
(con)        $a = 100,
(con)        $b = 200
(con)        )
(con)
(con)    $a + $b
(con)}
(out)
defaulttest 10
(out)210
(out)
defaulttest -b 10
(out)110

関数の終了

returnで関数を終了できる。これはnumパラメータが1000より大きいときに処理を中断する例である。

function returntest
(con){
(con)    param($num)
(con)
(con)    $num
(con)    if( $num -gt 1000 ){ return }
(con)    $num=$num*$num
(con)    return $num
(con)}

他の言語ではreturnといえば、関数の戻り値を指定する役割も持つ。しかしPowerShellではreturnの有無にかかわらず全てのステートメントの結果が戻り値として返されるので注意する。returntest関数は5行目と8行目に結果を返すステートメントがある。そのため、この関数の戻り値はreturn $numにある$numの値では無く、5行目と8行目の2つの値になる。

$result = returntest 100
$result
(out)100
(out)10000

入力処理メソッド

関数には名前付きのブロックを作成でき、これを使用するとパイプで渡される複数のオブジェクトを処理する関数が書ける。なおcleanブロックは7.3から追加された。

Function Test-Cmdlet
{
    Param ($param)
    dynamicparam{}
    begin{}
    process{}
    end{}
    clean{}
}

beginには前処理を書き、processには渡されたレコードごとに実行する処理を書く。そしてendには後処理を書く。

余りの値でフィルターする入力処理メソッドfilterremainderは以下のように書くことができる。

function filterremainder
(con){
(con)    param($mod, $remainder)
(con)    begin { "begin" }
(con)    process
(con)    {
(con)        if( $_ % $mod -eq $remainder ){ $_ }
(con)    }
(con)    end { "end" }
(con)}
(out)
1..10 | filterremainder -mod 3 -remainder 1
(out)
(out)begin
(out)1
(out)4
(out)7
(out)10
(out)end

通常の関数

名前付きのブロックが無い普通の関数は、endブロックに処理が書かれているものと見なされる。

スコープ

未記載

.NETオブジェクト

PowerShellで.NETオブジェクトを作成できる。StringBuilderで文字列を作成する例を示す。

$sb = New-Object System.Text.StringBuilder
$sb.Append("a")
(out)
(out)Capacity MaxCapacity Length
(out)-------- ----------- ------
(out)      16  2147483647      1
(out)
$sb.Append("text")
(out)
(out)Capacity MaxCapacity Length
(out)-------- ----------- ------
(out)      16  2147483647      5
(out)
$sb.ToString()
(out)atext

.NETオブジェクトのメソッド呼び出し

.NETオブジェクトのメソッド呼び出しはPowerShellスクリプトの関数と異なり括弧を付ける。

作成するオブジェクトのコンストラクタが引数を持つ場合、-ArgumentListで渡す。

$sb = New-Object System.Text.StringBuilder -ArgumentList "default",100
$sb.ToString()
(out)default

New-Objectの代わりにSystem.Text.StringBuilderクラスの静的new()メソッドを呼び出すこともできる。[クラス名]::静的メソッド()で静的メソッドを呼び出すことができ、newはコンストラクタを呼び出す静的メソッドである。

$sb = [System.Text.StringBuilder]::new("start")
$sb.ToString()
(out)start

ハッシュテーブルによる初期化構文

ハッシュテーブルを使ってオブジェクトを作成し、プロパティを設定することができる。まず、引数なしのコンストラクタを使ってオブジェクトを作成し、プロパティに値を設定するという挙動であるため、引数なしのコンストラクタがあること、パブリックなプロパティであることが使用できる条件である。

$sb = [System.Text.StringBuilder]::new()
$sb
(out)
(out)Capacity MaxCapacity Length
(out)-------- ----------- ------
(out)      16  2147483647      0
(out)
$sb = [System.Text.StringBuilder]@{Capacity=64}
$sb
(out)
(out)Capacity MaxCapacity Length
(out)-------- ----------- ------
(out)      64  2147483647      0

独自のオブジェクト

カスタムオブジェクト

PowerShellにはPSCustomObjectという独自の構造化データを簡単に作成する方法が用意されている。NameとPriceを持つデータを作成してみる。

$myObject = [PSCustomObject]@{
(con)    Name  = 'Orange'
(con)    Price = 1.25
(con)}
(out)
$myObject
(out)
(out)Name   Price
(out)----   -----
(out)Orange  1.25

後からメンバーを追加できる。

$myObject = [PSCustomObject]@{}
Add-Member -InputObject $myObject -MemberType NoteProperty -Name Name -Value "Orange"
$myObject
(out)
(out)Name
(out)----
(out)Orange

前述の通り、PowerShellスクリプトを実行するメンバーも定義できる。ポイントはMemberTypeをScriptProperty(またはScriptMethod)にすることだ。PropertyにはGetterとSetterを定義できるので、GetterをValue、SetterをSecondValueとしてAdd-Memberに渡している。Getterだけで避ければSecondValueは不要だ。

$myObject = [PSCustomObject]@{FamilyName='Yamada';FirstName='Taro'}
(out)
$FullNameGetter = {($this.FamilyName, $this.FirstName) -Join ' '}
$FullNameSetter = {($this.FamilyName, $this.FirstName) = $args[0] -Split ' ',2}
(out)
Add-Member -InputObject $myObject -MemberType ScriptProperty -Name FullName -Value $FullNameGetter -SecondValue $FullNameSetter
(out)
$myObject
(out)
(out)FamilyName FirstName FullName
(out)---------- --------- --------
(out)Yamada     Taro      Yamada Taro
(out)
(out)
$myObject.FullName = "Tanaka Kana"
$myObject
(out)
(out)FamilyName FirstName FullName
(out)---------- --------- --------
(out)Tanaka     Kana      Tanaka Kana
(out)

独自クラス

PSCustomObjectで物足りないなら独自クラスを作ったって良い。詳細な話はMSのドキュメントを参照するとして、例だけを示す。

class MyName : System.Object { # Objectを継承したMyNameクラス
    # メンバ変数
    [string] $FamilyName 
    [string] $FirstName = 'default' # デフォルト値付きメンバ変数
    hidden [string] $MiddleName     # 外から参照できないメンバ変数

    # クラスメソッド
    [bool] IsValid(){
        return [string]::IsNullOrWhiteSpace($this.FamilyName) -and [string]::IsNullOrWhiteSpace($this.FirstName)
    }

    # コンストラクタ
    MyName() : base() {}
    # PowerShellでは this() で他のコンストラクタを呼び出すことはできない。
    # 初期化するInitメソッドをhiddenで定義し、各コンストラクタから呼び出すと良い

    # 静的変数
    static [hashtable[]] $PropertyDefinitions = @(
        @{
            MemberType  = 'ScriptProperty'
            MemberName  = 'FullName'
            Value       = {($this.FamilyName, $this.FirstName) -Join ' '}               # Getter
            SecondValue = {($this.FamilyName, $this.FirstName) = $args[0] -Split ' ',2} # Setter
        }
    )

    # 静的コンストラクタ
    static MyName() {
        # プロパティの作成 (プロパティの定義はPropertyDefinitions)
        $TypeName = [MyName].Name
        foreach ($Definition in [MyName]::PropertyDefinitions) {
            Update-TypeData -TypeName $TypeName @Definition
        }
    }

    # 静的クラスメソッド
    static [string] ToFullName([string] $FamilyName, $FirstName){
        return ($FamilyName, $FirstName) -Join ' '
    }
}

実行ポリシー

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

MENU
PAGE TOP