PowerShellについて調べてみた

本記事について

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

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

本記事のコマンドはPowerShell 7.4.6で動作確認しました。そのため標準インストールのPowerShellでは動作しないコマンドが含まれています。

はじめに

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

パラメータの終了

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

Write-Outputコマンドで-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 というコマンドでも実行できる。

Get-Alias | where ResolvedCommandName -like "Get-ChildItem"
(out)
(out)CommandType     Name                                               Version    Source
(out)-----------     ----                                               -------    ------
(out)Alias           dir -> Get-ChildItem
(out)Alias           gci -> Get-ChildItem
(out)Alias           ls -> Get-ChildItem
(out)
ls
(out)
(out)    Directory: D:\
(out)
(out)Mode                 LastWriteTime         Length Name
(out)----                 -------------         ------ ----
(out)d-r--          2024/12/20    23:46                desktop
(out)d-r--          2024/12/28     0:51                downloads
(out)d-r--          2024/12/14    12:47                files
(out)d-r--          2024/12/10    23:06                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

プロパティ名と値のセットはハッシュテーブルで、値だけをもつパラメータは配列を使う。先ほどのGet-ChildItemの例はGet-ChildItem downloads -Filter *.pngとも書けるので、これをスプラッティングを使ってみる。

$ht = @{ Filter="*.png" }
$path = @( "downloads" )
Get-ChildItem @ht @path
(out)
(out)    Directory: D:\downloads
(out)
(out)Mode                 LastWriteTime         Length Name
(out)----                 -------------         ------ ----
(out)-a---          2023/05/19    16:08          14462 01147.png
(out)(省略)

スイッチパラメータ

スイッチパラメータでスプラッティングを使うときは$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演算子とよび、ある式の中に別の式やコマンドを埋め込むものである。

$no = 0
"$(++$no; $no) coins"
(out)1 coins

ヒア文字列リテラル

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

$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

数値など文字列以外と結合したい場合、最初に文字列以外があるとエラーになる。以下の例は数値+文字列を行おうとしており、PowerShellは文字列を数値へ変換しようとする。

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

Invoke-Expression

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

構文

コメント

# 行コメント

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

コメントベースヘルプ

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

<#
.<help keyword>
<help content>
#>

関数のヘルプコメントなら以下のように書く。コメント位置はこの例で使っている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] <Object>] [[-b] <Object>] [<CommonParameters>]
(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

match自動変数

-matchまたは-nomatchで正規表現に一致した場合、その結果が自動変数Matchesに格納される。

$address = "東京都新宿区西新宿N丁目"
$address -match '(?<A1>.*?[都道府県])(?<A2>.*?[市区町村])(.*)'
(out)True
$Matches
(out)
(out)Name                           Value
(out)----                           -----
(out)A1                             東京都
(out)A2                             新宿区
(out)1                              西新宿N丁目
(out)0                              東京都新宿区西新宿N丁目

コレクション検査

コレクションに含む-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メソッドが実装されているオブジェクトではEqualsメソッドの定義に従う。

文字列Nameだけを持つ2種類のオブジェクトNameObject(Equalsメソッドを持たない)とEquatableNameObject(Equalsメソッドを持つ)で-eq演算子の違いについて確認する。NameObjectではNameの値が一緒でも結果がFalseとなっていることが確認できる。

class NameObject {
(con)    [String]$Name
(con)}
(con)
class EquatableNameObject : System.IEquatable[Object] {
(con)    [String]$Name
(con)
(con)    [bool] Equals([Object] $obj) {
(con)        return $this.Name -ceq $obj.Name
(con)    }
(con)}
(con)
$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
大小関係の比較インタフェース(IComaparable)

オブジェクトの大小関係はSystem.IComparableインタフェースのCompareToメソッドを実装している場合のみ比較できる。

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 "パスが見つかりません。"
}

実行すると以下のようになり、パスが見つからないときにメッセージが表示されない。これはそもそも例外が発生していないためである。

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の代替構文

他の言語のような構文も使用できるが、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
スイッチパラメータ

機能のON/OFFを切り替えるようなスイッチパラメータを作成するには[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
パラメータの属性

パラメータの属性を定義し、あるパラメータを必須としたり、パラメータセットを定義できる。

Mandatory必須パラメータ。指定されない場合は入力を求める。
HelpMessage引数の簡単な説明。必須パラメータでヘルプを要求したとき、コメントベースヘルプが無いときにGet-Helpが呼び出されたときに利用される。
AllowNull
AllowEmptyString
AllowEmptyCollection
必須パラメータにおいて、null(または空文字、空のコレクション)を許容する。
Positionパラメータの位置指定。
ValueFromPipeline
ValueFromPipelineByPropertyName
パイプラインから入力を受け取る。ValueFromPipelineは入力オブジェクトそのものを値として、ByPropertyNameは入力オブジェクトにある同名のプロパティを値として受け取る。
ValueFromRemainingArguments定義されていない残りの全てのパラメータを受け取る。
Aliasパラメータの別名定義
Credential資格情報を受け取るパラメータ
パラメータセット

同じ処理でもパラメータの指定方法が何パターンかあるケースがある。パラメータセットを使うと、そのパターンを一つの関数で使い分けられる。

例として、以下のパラメータの組み合わせを受け取る関数を作成してみる。

パラメータ名組み合わせ1組み合わせ2組み合わせ3
Arg1必須無し無し
Arg2オプション無し無し
Arg3無し必須無し
Arg4無し無し必須
(out)function Test-ParameterSet1
(out){
(out)    [CmdletBinding(DefaultParameterSetName = 'Type1')]
(out)    param(
(out)        [Parameter(Mandatory,ParameterSetName="Type1")]
(out)        $Arg1,
(out)        [Parameter(ParameterSetName="Type1")]
(out)        $Arg2,
(out)        [Parameter(Mandatory,ParameterSetName="Type2")]
(out)        $Arg3,
(out)        [Parameter(Mandatory,ParameterSetName="Type3")]
(out)        $Arg4
(out)
(out)    )
(out)
(out)    Write-Host "ParameterSet:$($PSCmdlet.ParameterSetName)"
(out)}
Test-ParameterSet1 -Arg1 "a"
(out)ParameterSet:Type1
(out)

この例では各組み合わせに固有の必須パラメータがあり、どの必須パラメータが指定されているかでパラメータセットを識別しようとしている。どの組み合わせか識別できないようなパラメータを与えるとエラーとなる。

(out)function Test-ParameterSet1
(out){
(out)    [CmdletBinding(DefaultParameterSetName = 'Type1')]
(out)    param(
(out)        [Parameter(Mandatory,ParameterSetName="Type1")]
(out)        $Arg1,
(out)        [Parameter(ParameterSetName="Type1")]
(out)        $Arg2,
(out)        [Parameter(Mandatory,ParameterSetName="Type2")]
(out)        $Arg3,
(out)        [Parameter(Mandatory,ParameterSetName="Type3")]
(out)        $Arg4
(out)
(out)    )
(out)
(out)    Write-Output ($Arg1,$arg2,$Arg3,$arg4)
(out)}
Test-ParameterSet1 -Arg1 "a"
(out)a
(out)
Test-ParameterSet1 -Arg1 "a" -Arg3 "b"
(out)Test-ParameterSet1 : 指定された名前のパラメーターを使用してパラメーター セットを解決できません。
(out)発生場所 行:1 文字:1
(out)+ Test-ParameterSet1 -Arg1 "a" -Arg3 "b"
(out)+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(out)    + CategoryInfo          : InvalidArgument: (:) [Test-ParameterSet1]、ParameterBindingException
(out)    + FullyQualifiedErrorId : AmbiguousParameterSet,Test-ParameterSet1

パラメータの組み合わせだけではどのパラメータセットか識別するのを難しい場合は、パラメータセットを切り替えるスイッチパラメータを付与すると良い。下表の例で考える。

パラメータ名組み合わせ1組み合わせ2
Arg1必須オプション
Arg2オプション必須
Arg3無しオプション
Arg4無しオプション

この組み合わせの場合、Arg3またはArg4が指定されていれば組み合わせ2であることが特定できるが、Arg1またはArg2が指定された場合はどちらの組み合わせかを識別できない。そこで組み合わせ1をデフォルトとし、Mode2プロパティをスイッチパラメータとして追加する。

パラメータ名組み合わせ1(デフォルト)組み合わせ2
Arg1必須オプション
Arg2オプション必須
Arg3無しオプション
Arg4無しオプション
Mode2無し必須

これによりMode2プロパティが指定されているかどうかで組み合わせを識別できる。

バリデーション

パラメータが受け入れられる値や条件を指定できる。

ValidateCount(min,max)受け入れるCountプロパティの範囲を指定
ValidateLength(min,max)受け入れるLengthプロパティの範囲を指定
ValidatePattern(regex)受け入れる正規表現で文字列パターンを指定
ValidateRange(min,max)
ValidateRange(ValidateRangeKind)
受け入れる数値の範囲を指定
またはPositive、Negative、NonPositive、NonNegativeの指定も可能。
ValidateScript(script)受け入れる値をスクリプトブロックがTrueとなるように指定
ValidateSet(set)受け入れる値のリストを指定
ValidateNotNull()
ValidateNotNullOrEmpty()
ValidateNotNullOrWhiteSpace()
Null等を受け入れない

関数の終了

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ブロックはPowerShell 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()メソッドを呼び出すこともできる。[クラス名]::静的メソッド()で静的メソッドを呼び出すことができる。

$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 ' '
    }
}

スコープ

PowerShellではスコープという概念により、変数や関数を参照あるいは変更できる場所を制限する。スコープは入れ子構造となっており、外側のスコープを親スコープ、内側のスコープを子スコープと呼ぶ。

PowerShellではグローバル、ローカル、スクリプトの3つの名前付きスコープを持つ。

スコープ名意味
globalPowerShell セッション開始時に作成されるスコープ。PowerShellプロファイルで作成された変数やエイリアスもここに含まれる。
実行空間内のスコープのルートである。
local現在のスコープ
scriptスクリプトファイルが実行されるスコープ
  • アイテムは、明示的にプライベートにしない限り、作成されたスコープと子スコープから参照できる。
  • アイテムは、別のスコープを明示的に指定しない限り、作成されたスコープ内でのみ変更できる。
  • コードがアイテムを参照する場合、PowerShell はスコープ階層を検索する。
    • 現在のスコープでアイテムが見つからない場合、親スコープをたどって検索する。
    • ルートまでたどってもアイテムが見つからない場合、現在のスコープに新しい項目が作成される。
    • 一致したアイテムが祖先スコープで見つかった場合、検出されたスコープから値を取得する。
    • 一致したアイテムが祖先スコープで見つかり、その値を変更しようとした場合、アイテムが現在のスコープにコピーされる。変更は現在のスコープにのみ影響する。

他言語との違い

C系の言語であれば関数から呼び出し元の変数は参照できないが、PowerShellでは可能である。ただし、書き換えることは明示的に意図しない限りできないようになっている。

親スコープの変数を参照し、書き換える例を示す。

function HelloGoodbye
(con){
(con)    write-host $a
(con)    $a = 'Good bye!'
(con)    write-host $a
(con)}
(con)
$a = "Hello!"
HelloGoodbye
(out)Hello!
(out)Good bye!
write-host $a
(out)Hello!

スコープ修飾子

スコープ修飾子を使うと、アイテムのスコープを明示的に指定できる。グローバルスコープの変数を明示的に書き換える例を示す。

function HelloGoodbye
(con){
(con)    write-host $a
(con)    $global:a = 'Good bye!'
(con)    write-host $a
(con)}
$a = "Hello!"
HelloGoodbye
(out)Hello!
(out)Good bye!
write-host $a
(out)Good bye!

スコープ修飾子がない場合、すべてlocalとして扱われる。スコープ修飾子には単なるglobal、local以外に特殊な意味を持つ指定がある。

private:現在のスコープでのみ参照できる。
script:直近のスクリプトスコープ。見つからない場合はグローバルスコープ。
スクリプト内でこの指定で変数を作成すると、スクリプト内であればどこでも参照できる変数となる。
using:リモートセッションやスレッドジョブなどで、別のスコープて定義された変数を参照する。

親スコープの変数を参照・変更する例を示す。

function Write-A
(con){
(con)    write-host $a
(con)}
function New-A
(con){
(con)    $a = 'ParentScope'
(con)
(con)    Write-A
(con)}
New-A
(out)ParentScope

Write-Aメソッドは定義していない変数aを参照する。親スコープにある変数aの値が表示されている。ここでNew-Aメソッドの変数aをprivateに指定する。

function Write-A
(con){
(con)    write-host $a
(con)}
function New-A
(con){
(con)    $private:a = 'ParentScope'
(con)
(con)    Write-A
(con)}
New-A
(out)

すると、たとえ子スコープであっても変数aを参照できなくなり、何も表示されなくなる。

実行ポリシー

実行ポリシーによりスクリプトは実行を制御される。制御は厳密さ順に以下の通りとなる。

ポリシー意味詳細
Restricted制限全てのスクリプトを実行できない。
AllSigned署名必須信頼された発行元に署名されたスクリプトおよび構成ファイルのみ実行できる。署名の発行元が信頼済みか分類されていない場合、プロンプトが表示される。
RemoteSigned一部署名スクリプトは実行できる。ただし、インターネットからダウンロードされたスクリプトや構成ファイルには信頼された発行元による署名が必要である。
Unrestricted無制限全てのスクリプトを実行できる。ただし、ローカルコンピュータおよびローカルネットワーク以外のスクリプトや構成ファイルでは警告が表示される。
Bypass無検査全てのスクリプトを実行できる。

これとは別に、上記のいずれかに割り当てられるDefault、Undefinedという特殊なポリシーがある。

Defaultデフォルトで適用される実行ポリシー。
WindowsクライアントではRestricted、WindowsサーバではRemoteSignedと見なされる。
Undefined実行ポリシーが設定されていない。動作としてはDefaultと同様になる。

Zone.Identifier代替ストリーム

RemoteSignedはZone.Identifier代替ストリームを参照してどこから取得したファイルかを判定するため、代替ストリームを削除するとローカルファイルと見なされて署名が要求されなくなる。

ポリシーの確認・変更

この実行ポリシーは厳密なものではなく、スクリプトの不用意な実行を防ぐ意味合いが強い。ポリシーを変更すればスクリプトを実行できるようになるし、スクリプトファイルを開いて中身をコピペすることでも同じ結果となる。

Get-ExecutionPolicyを使うと現在のセッションに適用されるポリシーが表示される。Listプロパティを指定すると、マシンやユーザなどの設定一覧で確認できる。

Get-ExecutionPolicy -List
(out)
(out)        Scope ExecutionPolicy
(out)        ----- ---------------
(out)MachinePolicy       Undefined
(out)   UserPolicy       Undefined
(out)      Process       Undefined
(out)  CurrentUser    RemoteSigned
(out) LocalMachine       Undefined
(out)
(out)
Get-ExecutionPolicy
(out)RemoteSigned

Set-ExecutionPolicyを使うと適用するポリシーを変更できる。ただし、LocalMachineなど適用する範囲によっては管理者としてコマンドを実行しなければならない。

Set-ExecutionPolicy -Scope Process Unrestricted
Get-ExecutionPolicy -List
(out)
(out)        Scope ExecutionPolicy
(out)        ----- ---------------
(out)MachinePolicy       Undefined
(out)   UserPolicy       Undefined
(out)      Process    Unrestricted
(out)  CurrentUser    RemoteSigned
(out) LocalMachine       Undefined
(out)
(out)
Get-ExecutionPolicy
(out)Unrestricted
Set-ExecutionPolicy -Scope LocalMachine Unrestricted
(out)Set-ExecutionPolicy : レジストリ キー 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell
(out)' へのアクセスが拒否されました。 既定 (LocalMachine) のスコープの実行ポリシーを変更するには、[管理者として実行] オプシ
(out)ョンを使用して Windows PowerShell を起動してください。現在のユーザーの実行ポリシーを変更するには、"Set-ExecutionPolicy
(out)-Scope CurrentUser" を実行してください。
(out)発生場所 行:1 文字:1
(out)+ Set-ExecutionPolicy -Scope LocalMachine Unrestricted
(out)+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(out)    + CategoryInfo          : PermissionDenied: (:) [Set-ExecutionPolicy], UnauthorizedAccessException
(out)    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand

プロファイル

PowerShellセッションが開始するときに実行されるスクリプトをプロファイルという。Windowsでは既定で以下のプロファイルがサポートされ、上から順に実行される。

すべてのユーザー、すべてのホスト$PSHOME\Profile.ps1
すべてのユーザー、現在のホスト$PSHOME\Microsoft.PowerShell_profile.ps1
現在のユーザー、すべてのホスト$HOME\Documents\PowerShell\Profile.ps1
現在のユーザー、現在のホスト$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

よく使う処理のエイリアスを作成したり、プロンプトをカスタマイズできる。

自動変数・設定変数

PowerShellの状態を表す変数を自動変数、設定を表す変数を設定変数という。主なものを列挙する。

環境

OSやPowerShellなど環境の情報などが格納された変数。

$IsCoreCLR-
$IsLinuxLinuxのときTrue
$IsMacOSMacOSのときTrue
$IsWindowsWinsowsのときTrue
$PSVersionTablePowerShellのバージョン情報
$HOMEホームディレクトリ

設定

PowerShellの動作に影響を与える変数。

$MaximumHistoryCountセッションのコマンド履歴に記録できる数
$confirmPreference影響を与える操作の前に表示される確認メッセージの確認レベル(ConfirmImpact列挙体)
デフォルトはHigh(リスクの高いコマンドレットまたは関数を実行する前に確認する)
$DebugPreferenceデバッグメッセージ(Write-Debugコマンドなど)に対するPowerShellの応答方法(ActionPreference列挙体)
デフォルトはSilentlyContinue(デバッグメッセージを表示せず、継続して実行する)
$VerbosePreference詳細メッセージ ( Write-Verbose コマンドなど) に対する PowerShell の応答方法(ActionPreference列挙体)
デフォルトはSilentlyContinue(詳細メッセージを表示せず、継続して実行する)
$InformationPreference情報メッセージ ( Write-Information コマンドなど) に対する PowerShell の応答方法(ActionPreference列挙体)
デフォルトはSilentlyContinue(情報メッセージを表示せず、継続して実行する)
$WarningPreference警告メッセージ ( Write-Warning コマンドなど) に対する PowerShell の応答方法(ActionPreference列挙体)
デフォルトはContinue(警告メッセージを表示し、継続して実行する)
$ErrorActionPreferenceエラー(停止しないエラー)に対するPowerShell の応答方法(ActionPreference列挙体)
デフォルトはContinue(エラーメッセージを表示し、継続して実行する)

ActionPreference

ActionPreference列挙体は何かイベントが発生したときの対応方法を定義する列挙体である。

名前定義
SilentlyContinue0イベントを無視して処理を継続する。
Stop1コマンドを停止する。
Continue2イベントを通常のものとしてマークし、処理を継続する。
Inquire3処理の停止・続行を尋ねる。
Ignore4イベントを完全に無視する。ログ出力等もしない。
Suspend5将来用に予約
Break6デバッガーに入る

状態

$?最後にPowerShellが受けとったコマンド実行の成否
$LASTEXITCODE最後に実行したネイティブプログラムまたはPowerShellスクリプトの終了コード
$Error発生したエラーの履歴。最新のエラーは$Error[0]。
$Error.Clear()で履歴を消去できる。
$Event処理中のイベントを表す PSEventArgs オブジェクト
$PSItemパイプラインやForEachで処理対象のオブジェクト。
$PSItemのエイリアスとして$_も利用できる。
$foreach
$switch
ForEachおよびSwitchの列挙子
$Matches-match演算子または-nomatch演算子で、一致した場合のその結果(bool値またはハッシュテーブル)
一致しなかった場合は以前の値をそのまま保持し、Nullリセットなどはされない
$MyInvocationスクリプト、関数、スクリプトブロックを呼び出したスクリプトの情報
$argsスクリプト、関数、スクリプトブロックに渡された宣言されていないパラメーターの値の配列
$inputスクリプト、関数、スクリプトブロックに渡された入力
egin、process、endブロックでinputの内容は異なる
$^直前に受け取ったトークンの中で最初のもの
$$直前に受け取ったトークンの中で最後のもの

トークン

トークンとはPowerShellが入力を解析して区切ったときの、1つのまとまりのことを指す。英語でいう単語のようなもの。

a test command "string with space"
(out)a: The term 'a' is not recognized as a name of a cmdlet, function, script file, or executable program.
(out)Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
$^
(out)a
$$
(out)string with space

コメントを残す

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