$null と真偽判定のPowerShell 固有の落とし穴を避けられる# JavaScript # PowerShell
const name = "PS"; $name = "PS"
let count = 0; $count = 0
const arr = [1, 2, 3]; $arr = @(1, 2, 3)
const obj = { a: 1 }; $obj = @{ a = 1 }
console.log(name); Write-Host $name
見た目の違いは $ が付くことと 宣言キーワードが無いことの2点です。
| 観点 | JavaScript | PowerShell |
|---|---|---|
| 宣言 | const / let が必要 | 不要。代入した時点で存在する |
$ の意味 | — | 変数名の一部。宣言記号ではない |
| 再代入の禁止 | const | 相当するものは実質なし |
| 型 | 値に型がある(動的) | .NET の型が付く。変数に型制約も付けられる |
PowerShell の値は、内部的にはすべて .NET の型を持っています。確認してみます。
$s = "hello"
$n = 42
$a = @(1, 2, 3)
$s.GetType().Name # String
$n.GetType().Name # Int32
$a.GetType().Name # Object[]
さらに 変数に型を宣言できます。これは JavaScript には無い機能です。
[int]$num = 5
$num = "42" # 文字列だが自動で int に変換される → 42
$num = "abc" # 変換できないのでエラーになる
[int]$x は実行時に効き続けます。代入のたびに変換が試みられ、失敗すればその場でエラーになります。入力値の検証を型に任せられる、という使い方ができます。
| 型 | 用途 | 例 |
|---|---|---|
[int] / [long] | 整数 | [int]$count = 0 |
[double] / [decimal] | 小数(金額は decimal) | [decimal]$price = 1980 |
[string] | 文字列 | [string]$name = "PS" |
[bool] | 真偽値 | [bool]$ok = $true |
[datetime] | 日時 | [datetime]$d = "2026-08-22" |
[string[]] | 文字列の配列 | [string[]]$names = @() |
[int]$x = 5 は int x = 5; とほぼ同じ感覚で読めます。違いは宣言しないこともできる点だけです。型を書かなければ、代入された値の型がそのまま採用されます。Dim x As Integer にあたるのが [int]$x です。Option Explicit のような「宣言必須」の仕組みは既定では無効ですが、Set-StrictMode -Version Latest を書くと、未定義変数の参照がエラーになります。
PowerShell ではシングルクォートとダブルクォートが別物です。ここは最初に必ず覚えてください。
$name = "PowerShell"
'こんにちは $name' # → こんにちは $name (そのまま)
"こんにちは $name" # → こんにちは PowerShell(展開される)
変数名だけならそのまま書けますが、プロパティやメソッドは $() で囲みます。
$file = Get-Item .\a.txt
"名前は $file.Name です" # → 名前は a.txt.Name です ✗ 意図しない
"名前は $($file.Name) です" # → 名前は a.txt です ✓
| やりたいこと | JavaScript | PowerShell |
|---|---|---|
| 変数の埋め込み | `Hello ${name}` | "Hello $name" |
| 式の埋め込み | `${obj.prop}` | "$($obj.prop)" |
| 展開しない | 'Hello ${name}' | 'Hello $name' |
PowerShell のエスケープ文字は バッククォート(`)です。バックスラッシュはただの文字として扱われます。
"C:\temp\new".Length # → 11 \t や \n は展開されない
"C:\temp`nnew" # → 改行が入る(`n が改行)
"C:\\temp\\new" と二重に書く必要がありました。PowerShell ではパスをそのまま貼り付けられます。「エスケープが面倒だからパスを書き間違える」という事故が起きません。
| エスケープ | 意味 | JavaScript での対応 |
|---|---|---|
| `n | 改行 | \n |
| `t | タブ | \t |
| `$ | $ そのもの(展開させない) | — |
| `` | バッククォートそのもの | \\ |
| `" | ダブルクォート | \" |
$sql = @'
SELECT *
FROM users
WHERE name = '田中'
'@
'@ の前にスペースやタブが1つでもあると構文エラーになります。インデントを揃えたくなる場所ですが、ここだけは字下げできません。開始側の @' は行末で改行する必要があります。
# ✗ エラーになる例
$s = @'
テキスト
'@ ← 終端がインデントされている
# ✓ 正しい
$s = @'
テキスト
'@ ← 終端は必ず行頭
$ があるか」で決めると覚えると簡単です。正規表現・JSON・パスなど $ や ` を含む文字列は、シングルクォートかヒアストリング @'...'@ に入れておけば、勝手に展開される事故を防げます。== や > を使わないのかPowerShell はシェルなので、> はリダイレクトとして予約されています。そのため比較演算子はハイフン付きの英字になっています。
| PowerShell | 意味 | JavaScript |
|---|---|---|
| -eq | 等しい | === |
| -ne | 等しくない | !== |
| -gt / -ge | より大きい / 以上 | > / >= |
| -lt / -le | より小さい / 以下 | < / <= |
| -and / -or / -not | 論理演算 | && / || / ! |
| -like | ワイルドカード一致 | — |
| -match | 正規表現一致 | RegExp.test() |
| -contains / -in | 要素が含まれるか | Array.includes() |
| -replace | 正規表現で置換 | String.replace() |
ここが本章でもっとも事故につながる仕様です。
"abc" -eq "ABC" # → True (!)
"abc" -ceq "ABC" # → False (c を付けると区別する)
c 付きを使ってください。readme.html → README.html のような変更で、-ne による「変わったか」判定を使うと「変更なし」と誤判定してスキップされます。-cne / -creplace に置き換えれば正しく動きます。
| 既定(区別しない) | 区別する | 明示的に区別しない |
|---|---|---|
| -eq / -ne | -ceq / -cne | -ieq / -ine |
| -like / -notlike | -clike / -cnotlike | -ilike / -inotlike |
| -match / -notmatch | -cmatch / -cnotmatch | -imatch / -inotmatch |
| -replace | -creplace | -ireplace |
i は insensitive(区別しない)、c は case-sensitive(区別する)の頭文字です。接頭辞なしは i と同じです。
"report-2026.txt" -like "report-*.txt" # → True (ワイルドカード)
"report-2026.txt" -match "report-\d{4}" # → True (正規表現)
-match が成功すると、キャプチャ結果が $matches に入ります。
if ("report-2026.txt" -match "report-(\d{4})") {
$matches[1] # → 2026
}
-like は * と ? だけの簡易な照合、-match は本格的な正規表現です。ファイル名の絞り込みには -like、文字列から値を取り出すには -match と使い分けます。-match の結果が入る $matches は自動変数で、マッチのたびに上書きされます。ループの中で使うときは、その場で別の変数に取り出してください。
# JavaScript # PowerShell
const a = [1, 2, 3]; $a = @(1, 2, 3)
$a = 1, 2, 3 # @() は省略できる
const empty = []; $empty = @()
a[0] $a[0]
a[a.length - 1] $a[-1] # 負のインデックスが使える
a.length $a.Count
a.slice(1, 3) $a[1..2] # 範囲指定
カンマ , が配列を作る演算子なので、@() は無くても動きます。ただし要素が1個のときは @() が必要です。
$one = @(5) # 要素1個の配列
$one.GetType().Name # → Object[] (配列のまま)
+= は毎回新しい配列を作る 性能の罠PowerShell の配列は固定長です。+= は要素を追加しているように見えますが、実際は新しい配列を作って全要素をコピーしています。
+= はコピーの繰り返し。件数が増えると急激に遅くなる# ✗ 件数が多いと遅い
$result = @()
foreach ($x in 1..10000) { $result += $x }
# ✓ List を使う
$list = [System.Collections.Generic.List[object]]::new()
foreach ($x in 1..10000) { $list.Add($x) }
# ✓ パイプラインに流す(PowerShell らしい書き方)
$result = foreach ($x in 1..10000) { $x }
foreach の中で出力された値はまとめて左辺の変数に入ります。明示的に配列を組み立てる必要がありません。この「出力が自動で集まる」性質は 05 章のパイプラインと同じ仕組みで、慣れると最も短く書けます。
# JavaScript # PowerShell
const o = { a: 1, b: 2 }; $o = @{ a = 1; b = 2 }
o.a $o.a
o["a"] $o["a"]
o.c = 3; $o.c = 3
Object.keys(o) $o.Keys
delete o.a; $o.Remove("a")
"a" in o $o.ContainsKey("a")
区切りはカンマではなくセミコロン、代入はコロンではなくイコールです。ここは書き間違えやすい箇所です。
[ordered]$h = @{ b = 2; a = 1 } # 順序は保証されない
$h = [ordered]@{ b = 2; a = 1 } # 書いた順に並ぶ
[ordered] を付けてください。HashMap、C# の Dictionary、VBA の Scripting.Dictionary にあたります。$o.a と $o["a"] は同じ)。キーに空白や記号が入るときだけブラケットが必要です。
$null は左辺に置く 作法PowerShell では $null との比較を必ず左辺に書きます。
if ($null -eq $value) { } # ✓ 正しい
if ($value -eq $null) { } # ✗ 避ける
理由は、右辺が配列だと -eq がフィルタとして働くためです。
$arr = @(1, $null, 2)
$arr -eq $null # → $null が1個返る(フィルタ結果)
$null -eq $arr # → False(比較結果)
-eq は左辺がコレクションだと「絞り込み」になります。@(1,2,3) -eq 2 は True ではなく 2 を返します。これは 05 章のパイプラインと同じ「コレクションを流す」思想の現れですが、$null チェックでは意図しない動作になります。左辺に $null を置けば必ず比較になるので、そう書く習慣をつけてください。
実際に [bool] で変換した結果です。
[bool] で変換した結果。空配列と空ハッシュテーブルの非対称に注意| 式 | PowerShell | JavaScript の Boolean() | 備考 |
|---|---|---|---|
| "" | False | false | 同じ |
| "0" | True | true | 同じだが直感に反する |
| "false" | True | true | 設定値の判定で事故りやすい |
| 0 | False | false | 同じ |
| @() / [] | False | true | 逆になる |
| @{} / {} | True | true | 配列と非対称 |
| $null / null | False | false | 同じ |
if ([]) が真になるため arr.length で判定する習慣がありますが、PowerShell では if ($arr) がそのまま「要素があるか」の判定になります。ただし要素が1個で、その値が 0 や "" だと False になります。確実に件数で判定したいなら if ($arr.Count -gt 0) と書いてください。
"42" + 1 # → "421" 文字列結合(左が文字列)
1 + "42" # → 43 数値加算(左が数値)
JavaScript ではどちらも文字列結合("421" と "142")になります。PowerShell は左辺の型に合わせて右辺を変換するので、結果が変わります。
"42" + 1 の形になり、意図しない文字列結合が起きます。[int]$row.Amount + 1 のように明示的に変換するのが安全です。
-as[int]"abc" # → エラーで止まる
"abc" -as [int] # → $null が返る(止まらない)
$n = $input -as [int]
if ($null -eq $n) { Write-Host "数値ではありません" }
| やりたいこと | 書き方 | 失敗したとき |
|---|---|---|
| 確実に変換したい | [int]$x | 例外で停止する |
| 失敗を許容したい | $x -as [int] | $null が返る |
| 型を調べたい | $x -is [int] | True / False |
-as は C# の as 演算子と同じ発想で、変換できなければ例外ではなく $null を返します。ユーザー入力やファイルから読んだ値の検証に向いています。[int]$x は「変換できて当然」という前提の書き方です。失敗したらその場で止まってほしいときに使います。どちらを使うかは、失敗を想定しているかどうかで決めてください。
| やりたいこと | PowerShell | 注意点 |
|---|---|---|
| 変数に型を付ける | [int]$x = 5 | 実行時に効き続ける。代入のたびに変換される |
| 変数を埋め込む | "名前は $($o.Name)" | プロパティは $() で囲む |
| パスを書く | 'C:\temp\new' | エスケープは \ でなく `。そのまま書ける |
| 大小文字を区別して比較 | -ceq / -cne / -creplace | 既定は区別しない |
| 配列に貯める | $r = foreach (...) { ... } | += は件数が増えると遅い |
| 順序を保つ辞書 | [ordered]@{ } | 通常の @{} は順序を保証しない |
$null の判定 | if ($null -eq $x) | 左辺に置く |
| 安全な型変換 | $x -as [int] | 失敗すると $null |
c を付ける。付け忘れは「変更なし」の誤判定を生む"42" + 1 は "421"、1 + "42" は 43。外部入力は明示的に変換するGet-Member ひとつで、これが使えるようになると、知らないコマンドでも自力で調べられるようになります。以降の章の学習速度が変わる回です。