PowerShellのスクリプトを書き始めると真っ先に登場するのが変数です。$を付けて名前を決めるだけなので簡単そうに見えますが、いざ使ってみると文字列に埋め込むところで思った表示にならなかったり、代入したはずの値がいつの間にか消えていたりすることってありますよね。
この記事では変数の宣言から型の指定、スコープの違い、文字列への埋め込み方まで、実際にコマンドを動かしながら確認した内容を中心に解説します。特に文字列展開のところは実際に試してみると地味にハマりやすいポイントがあります。
PowerShellの変数は$変数名 = 値で作成し$変数名で参照します。型は代入した値から自動的に決まりますが、[int]のように型を指定することも可能です。文字列へ埋め込むときはダブルクォートを使い、変数名の直後に文字やコロンが続く場合は${変数名}で区切ります。
- OS
Windows 11 Home 25H2
- PowerShell
7.6.5
※変数まわりの動作はWindows・macOS・Linuxで共通の言語仕様のため環境が違っても結果はほぼ変わりません。$env:USERNAMEなどはWindows固有の項目です。
PowerShellの変数とは
変数は値を一時的に保存しておく入れ物です。PowerShellでは変数名の先頭に$を付けて表します。
$name = "Chun"$nameという名前の箱に"Chun"という文字列を入れているイメージです。一度代入した値は同じセッション内であれば何度でも呼び出せます。
$nameChun型を宣言しなくてもそのまま使えるのがPowerShellの特徴です。この点は後述の「型を確認・指定する」で改めて触れます。
変数を宣言して値を取得する
文字列だけでなく数値や真偽値もそのまま代入できます。
$str = "Hello, PowerShell"
$num = 100
$bool = $true値を確認する方法としてWrite-HostとWrite-Outputのふたつがよく使われますが、この2つは似ているようで役割が違います。
Write-Host $str
Write-Output $numHello, PowerShell
100見た目の出力結果は同じに見えますが実際に動かして比べてみると挙動に差がありました。
$resultA = Write-Host "test"
$resultB = Write-Output "test"
"resultA is: [$resultA]"
"resultB is: [$resultB]"test
resultA is: []
resultB is: [test]

Write-Hostは画面表示向けのコマンドで、Write-Outputが使う成功出力ストリームにはオブジェクトを流しません。そのため、この例では$resultAに値が入りません。PowerShell 5.0以降のWrite-Hostは情報ストリームへ書き込むため、情報ストリームとして抑止やリダイレクトが可能です。一方、Write-Outputは成功出力ストリームへ値を送り、変数への代入や後続のパイプライン処理に利用できます。後で値を使う場合は、Write-Outputまたは式をそのまま出力します。
変数の値を変更・削除する
値を再代入する
変数の値は代入し直すだけで上書きされます。
$name = "1st Chun"
$name = "2nd Chun"
$name2nd ChunClear-Variableで値だけを消す
値だけを空にしたい場合はClear-Variableを使います。
$name = "Chun"
Clear-Variable -Name name
"Clear-Variable後: [$name]"Clear-Variable後: []値は空になりましたが変数自体は残っています。Get-Variableで確認すると存在していることがわかります。
Get-Variable -Name name -ErrorAction SilentlyContinue | ForEach-Object { "あるよ!" }あるよ!Remove-Variableで変数ごと消す
変数そのものを削除したい場合はRemove-Variableを使います。
$name2 = "Chun"
Remove-Variable -Name name2
Get-Variable -Name name2 -ErrorAction SilentlyContinue | ForEach-Object { "あるよ!" }(何も表示されない = 存在しない)Clear-Variableは「値だけ空にする」、Remove-Variableは「変数そのものをなくす」という違いですね。削除した変数をそのまま参照してもエラーにはならず、黙って空の値($null扱い)が返ってくるだけでした。これは後述の「よくある間違い」でも触れる、PowerShellの少し独特な挙動です。
なお$name = $nullとしても見た目の結果はClear-Variableとほぼ同じになります。$nullの代入は値そのものを書き換える操作なのに対し、Clear-Variableは「値のリセット」を意図した専用のコマンドレットです。スクリプトの意図を明確にしたい場合はClear-Variableを使うのがおすすめです。
参考:Microsoft公式のRemove-Variable
文字列の中へ変数を埋め込む
ダブルクォートとシングルクォートの違い
PowerShellでは文字列の展開がダブルクォートとシングルクォートで変わります。
$name = "Chun"
"Hello, $name"
'Hello, $name'Hello, Chun
Hello, $nameダブルクォートは変数を展開する「展開文字列」、シングルクォートは書いた通りに扱う「リテラル文字列」です。変数をそのまま文字として表示したい場合はシングルクォートを使うと覚えておくとよいです。
$()で式やプロパティを展開する
プロパティやメソッドの結果、計算式などを文字列に埋め込みたいときは$()(サブ式演算子)を使います。
$now = Get-Date
"現在時刻は $($now.ToString("HH:mm:ss")) です"
"1 + 1 の結果は $(1 + 1) です"現在時刻は 18:14:33 です
1 + 1 の結果は 2 です$now.ToString(...)のようにプロパティやメソッドを呼び出す場合は$()で囲む必要があります。"$now.ToString(...)"のように直接書いてしまうと.ToString(...)の部分がただの文字として扱われてしまうので注意してください。
コロンや日本語が直後に続くと起きること
変数名の直後に別の文字が続く場合、区切りが曖昧になって思わぬ結果になることがあります。
まずはコロンのケースです。
$name = "Chun"
"$name:test"(何も表示されない)エラーにもならず、ただ空文字が返ってきました。PowerShellは$name:の部分を「nameという名前のスコープにあるtestという変数」($スコープ名:変数名という書き方)だと解釈してしまうため、該当するスコープが存在せず黙って空の値になってしまうようです。
さらに紛らわしいのが、コロンの直後に続く文字によって挙動が変わる点です。Microsoft公式ドキュメントにある$HOME:を使った例では、コロンの直後が半角スペースだとパースエラーになりました。
"$HOME: where the heart is."ParserError:
Line |
1 | "$HOME: where the heart is."
| ~~~~~~
| Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to
| delimit the name.⚠️ 注意 同じ「コロンの後に文字が続く」パターンでも、変数名として解釈できる文字が続くと
$name:testのように黙って空文字になり、変数名として使えない文字(スペースなど)が続くとエラーになるという違いがあります。地味に厄介なのは前者で、エラーにならない分バグに気づきにくいところです。
日本語ならではの落とし穴もありました。ひらがなやカタカナも変数名に使える文字として扱われるため、変数のすぐ後ろに日本語を続けると意図しない結果になります。
$name = "Chun"
"$nameさん"(何も表示されない)これは$nameさん全体をひとつの変数名として解釈してしまうためです(ひらがなも変数名に使える文字に含まれます)。日本語の文章に変数を埋め込むとき、地味にハマりやすいパターンだと思います。
対策は${}で変数名を明示的に区切ることです。
$name = "Chun"
"${name}:test"
"${name}さん"Chun:test
Chunさん変数の直後に文字や記号を続けて文章にする場合は、とりあえず${変数名}の形にしておくと安心です。半角スペースを1つ挟むだけでも回避できますが、レイアウトの都合でスペースを入れたくない場面もあると思うので${}で囲む方法を覚えておくと便利です。
参考:Microsoft公式のabout_Quoting_Rules
PowerShellの変数の型を確認・指定する
GetType()で型を確認する
代入した値の型はGetType()メソッドで確認できます。
$count = 10
$count.GetType()IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType文字列を代入した場合はString型になります。
$name = "Chun"
$name.GetType()IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.ObjectPowerShellの変数は宣言時に型を指定しなくても代入した値に応じて自動的に型が決まる「動的型付け」です。ひとつの変数に別の型の値を入れ直すこともできてしまいます。
[string]や[int]で型を指定する
変数の型を固定したい場合は変数名の前に[型名]を付けます。
[int]$count = 10
$count.GetType()IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType数値に変換できる文字列であれば代入時に自動で変換してくれます。
[int]$count = "20"
"文字列'20'を代入 -> $count ($($count.GetType().Name))"文字列'20'を代入 -> 20 (Int32)型に合わない値を代入したときの動き
数値に変換できない文字列を代入しようとするとエラーになります。
[int]$count2 = "abc"MetadataError: Cannot convert value "abc" to type "System.Int32". Error: "The input string 'abc' was not in a correct format."⚠️ 注意 実際に試してみると、このエラーはスクリプトの実行自体を止めるものではなく、エラーメッセージを表示したまま次の行の処理へ進んでしまいました。「エラーが出ているのに処理が続いている」という状態に気づきにくいので、型を指定した変数へ外部入力(CSVの値やユーザー入力など)を代入する場合は、あらかじめ
try/catchで挟んでおくと安心です。
型の詳しい仕様はMicrosoft公式のabout_Variablesにもまとまっているので、より詳しく知りたい方はあわせてチェックしてみてください。
変数のスコープを理解する
変数には「どこから参照できるか」を示すスコープという概念があります。
| スコープ | 説明 |
|---|---|
| ローカル | 現在のスコープです。関数の中で作った変数は基本的にこのスコープになります。 |
| スクリプト | スクリプトファイル全体で有効なスコープです。$script:を付けて明示できます。 |
| グローバル | セッション全体で有効なスコープです。$global:を付けて明示できます。 |
関数の中で変数を作ると関数の外の同名変数には影響しません。
$name = "スクリプト全体のChun"
function Show-Name {
$name = "関数内だけのChun"
"関数内での値: $name"
}
Show-Name
"関数呼び出し後、スクリプト側の値: $name"関数内での値: 関数内だけのChun
関数呼び出し後、スクリプト側の値: スクリプト全体のChun関数の中で$nameに代入していますが、これは関数内だけで有効な別の変数として扱われるためスクリプト側の$nameは変わりません。
.ps1スクリプト内の関数から、そのスクリプトのスコープにある変数を更新する場合は$script:を付けます。次の例はコードを.ps1ファイルとして保存し、スクリプトとして実行することを前提にしています。
$counter = 0
function Add-Counter {
$script:counter = $script:counter + 1
"関数内でのカウンター: $script:counter"
}
Add-Counter
Add-Counter
"スクリプト側のカウンター最終値: $counter"関数内でのカウンター: 1
関数内でのカウンター: 2
スクリプト側のカウンター最終値: 2$script:を付けることで関数呼び出しをまたいでカウンターの値を積み上げられるようになりました。単純なカウンターやフラグを関数間で共有したいときによく使う書き方です。スコープの詳しい仕様はMicrosoft公式のabout_Scopesで確認できます。
環境変数と通常の変数の違い
$env:Pathのようにenv:が付いた変数を見かけたことがあるかもしれませんが、これは通常の変数とは別物です。OS側が管理する環境変数を参照・設定するための書き方になります。
以下はWindowsでの例です。macOSやLinuxではユーザー名の取得に$env:USERが使われることが多く、環境変数名の大文字と小文字も区別されます。
$name = "Chun"
$env:MY_APP_NAME = "ChunLog"
"通常の変数 name : $name"
"環境変数 MY_APP_NAME : $env:MY_APP_NAME"
"env:を付けずに参照した場合 : $MY_APP_NAME"通常の変数 name : Chun
環境変数 MY_APP_NAME : ChunLog
env:を付けずに参照した場合 :$env:を付けずに$MY_APP_NAMEと書いてもそれは環境変数とは無関係の別の(未定義の)変数として扱われます。名前が似ているだけでまったくの別物なので、環境変数のつもりで$だけを付けて参照してしまうと空振りすることになります。
環境変数の詳しい仕組みやPathの優先順位については以下の記事で解説しているのであわせて確認してみてください。


変数を使うときによくある間違い
変数名のスペルミス
存在しない変数を参照してもエラーにはならず、黙って空($null)が返ってきます。タイプミスに気づきにくい原因のひとつです。
$myValue = "test"
"スペルミスした変数を参照: [$myVal]"スペルミスした変数を参照: []数値のつもりが文字列になっている
文字列と数値を+で結合するとき、左側の変数の型によって結果が変わります。
$a = "5"
$b = 3
"文字列'5' + 数値3 = $($a + $b)"
$c = 5
$d = "3"
"数値5 + 文字列'3' = $($c + $d)"文字列'5' + 数値3 = 53
数値5 + 文字列'3' = 8左が文字列だと文字列結合(53)に、左が数値だと数値の加算(8)になります。CSVやユーザー入力から読み込んだ値は文字列型であることが多いので、計算に使う前に[int]などで型変換しておくと事故を防げます。
変数を使った簡単なスクリプト例
ここまでの内容を組み合わせた簡単な例です。フォルダーのパスと繰り返し回数を変数にして、簡易的なメッセージを表示します。
$folderPath = "C:\Work\Reports"
$userName = $env:USERNAME
$repeatCount = 3
"対象フォルダ: $folderPath"
"実行ユーザー: $userName"
for ($i = 1; $i -le $repeatCount; $i++) {
"${i} 回目の処理を実行中です(${userName} さんの ${folderPath} に対して)"
}$env:USERNAMEはWindowsでログイン中のユーザー名が自動的に入る環境変数です。ファイルパスやユーザー名のような「後で変わるかもしれない値」を変数にしておくと、スクリプトの使い回しがしやすくなります。
CSVファイルを読み込みながら変数を使う実践的な例は以下でも紹介しているので興味があれば見てみてください。


作成したスクリプトを.ps1ファイルとして保存して実行しようとすると実行ポリシーのエラーに当たることがあります。その場合は以下の記事も参考にしてください。


よくある質問
まとめ
- 変数は
$変数名 = 値で宣言できて型は自動で決まるよ。 - 値だけ消すなら
Clear-Variable、変数ごと消すならRemove-Variableを使い分けてね。 - 文字列に埋め込むときはダブルクォートを使って
$()でプロパティや式も展開できるよ。 - 変数名の直後にコロンや日本語を続けるとうまく展開されないことがあるから
${}で囲むと安心だよ。 - スコープを意識しないと関数の中と外で値が食い違うことがあるから
$script:も覚えておいてね。
PowerShellの基本操作やファイル操作、ログ監視、文字列検索などもまとめて確認したい場合はこちら。






