コマンドプロンプトから PowerShell を実行する
コマンドプロンプトだけでは難しい処理(ZIP 圧縮、日付の書式変換、JSON の解析など)は、PowerShell をコマンドプロンプトから呼び出すことで実現できます。powershellコマンドを使えば、バッチファイルの中にも PowerShell の処理を組み込めます。
この記事では、powershellコマンドの基本的な使い方と、つまずきやすい引用符・エスケープ・実行ポリシーの扱いを解説します。
powershellコマンドの基本構文
powershell [-NoProfile] [-ExecutionPolicy <ポリシー>] -Command "<コマンド>"
powershell [-NoProfile] [-ExecutionPolicy <ポリシー>] -File <スクリプトのパス> [引数...]
コマンドプロンプトでpowershellだけを入力すると、プロンプトがPS C:\>に変わり、PowerShell の対話モードに入ります。元に戻るにはexitと入力します。
Windows に標準で入っているpowershellは Windows PowerShell 5.1 です。別途インストールする PowerShell 7 以降はpwshコマンドで起動します。この記事のオプションは、どちらでも同じように使えます。
コマンドを1つだけ実行する(-Command)
-Commandにコマンドを渡すと、実行後にコマンドプロンプトへ戻ります。
powershell -Command "Get-Date"
-Commandは省略できます(powershell "Get-Date")。ただし、引数の解釈が分かりにくくなるため、明示しておくのがおすすめです。
PowerShell のコマンド(コマンドレット)はGet-ChildItemのような「動詞-名詞」形式が基本です。たとえば、カレントフォルダーのファイルを更新日時の新しい順に5件だけ表示するには、次のようにします。
powershell -NoProfile -Command "Get-ChildItem | Sort-Object LastWriteTime -Descending | Select-Object -First 5 Name, LastWriteTime"
|が二重引用符"の内側にあるため、コマンドプロンプトではなく PowerShell のパイプとして処理されます。
よく使うオプション
| オプション | 説明 |
|---|---|
-Command | 指定した文字列を PowerShell のコマンドとして実行する |
-File | 指定した.ps1スクリプトを実行する |
-NoProfile | プロファイル(起動時の設定スクリプト)を読み込まない。起動が速くなり、環境差も出にくい |
-NonInteractive | ユーザーへの入力待ちをしない。確認が必要な処理はエラーになる |
-ExecutionPolicy <ポリシー> | このプロセスだけ実行ポリシーを指定する(Bypassなど) |
-NoExit | コマンドを実行した後も PowerShell を終了しない |
-EncodedCommand | UTF-16LE の文字列を Base64 でエンコードしたコマンドを渡す(引用符の問題を避けたいとき) |
バッチファイルから呼び出すときは、-NoProfileを付けておくと安定します。
スクリプトファイルを実行する(-File)
処理が長くなる場合は、PowerShell の処理を.ps1ファイルに分けて-Fileで実行するのが安全です。
param([string]$Name = 'World')
Write-Host "Hello, $Name!"
powershell -NoProfile -ExecutionPolicy Bypass -File .\hello.ps1 -Name Taro
実行ポリシーのエラーが出たとき
.ps1ファイルを実行しようとして「このシステムではスクリプトの実行が無効になっているため…」というエラーが出る場合は、実行ポリシーが原因です。-ExecutionPolicy Bypassを付けると、そのプロセスに限ってポリシーを回避できます。システム全体の設定は変更されません。
実行ポリシーが制限するのは.ps1ファイルの実行です。-Commandで渡した文字列そのものは、実行ポリシーの制限を受けません。ただし、-Commandの中から.ps1を呼び出す場合は、そのスクリプトにポリシーが適用されます(-NoProfileを付けないと、プロファイルの読み込みでもエラーになることがあります)。また、-ExecutionPolicy Bypassは信頼できるスクリプトにだけ使いましょう。組織の管理者がグループポリシーで実行ポリシーを設定している場合は、-ExecutionPolicyで上書きできません。
引用符・特殊文字の扱い
コマンドプロンプトと PowerShell の両方を通るため、特殊文字の扱いに注意が必要です。
| 文字 | コマンドプロンプト(cmd)での注意 | 対処 |
|---|---|---|
" | -Commandの全体を囲むのに使う | 内側では'(一重引用符)を使うと楽 |
% | バッチファイル内では変数展開される | %%と書く |
| & > | 二重引用符の外にあると cmd が解釈する | 二重引用符の中に入れる、または^で退避 |
$ | cmd では特別な意味なし。PowerShell の変数として扱われる | そのまま書ける |
! | 遅延展開が有効なとき消える | 遅延展開を無効にして実行する |
最も簡単な対策は、コマンド全体を"で囲み、中の文字列リテラルには'を使うことです。
powershell -NoProfile -Command "Write-Host 'こんにちは' ; (Get-Date).ToString('yyyy-MM-dd')"
"をどうしても内側で使う場合は、\"と書くことで渡せます。
powershell -NoProfile -Command "Write-Host \"現在の時刻: $((Get-Date).ToString('HH:mm'))\""
エスケープが2重3重になって読みにくくなってきたら、-Commandで頑張らずに.ps1ファイルに切り出して-Fileで実行しましょう。保守もしやすくなります。
実行結果をバッチファイルで使う
出力を変数に格納する
for /fを使うと、PowerShell の出力をバッチファイルの変数に取り込めます。バッチファイルの中では%を%%にします。
@echo off
for /f "usebackq delims=" %%d in (`powershell -NoProfile -Command "Get-Date -Format yyyyMMdd_HHmmss"`) do set TIMESTAMP=%%d
echo %TIMESTAMP%
for /fにusebackqを付けると、コマンドを逆引用符(`)で囲めるようになります。PowerShell 側で一重引用符'を使っても、for /fの構文と衝突しません。
コマンドプロンプトの%date%は地域設定によって形式が変わりますが、PowerShell のGet-Date -Formatなら設定に左右されず同じ形式の文字列を得られます。
終了コードを受け取る
-Commandで実行した場合、最後に実行したコマンドが成功すれば0、失敗すれば1がERRORLEVELに入ります。外部プログラムの終了コードはそのまま引き継がれないことがあるため、確実に伝えたいときや細かく制御したいときは、exitで終了コードを指定します。
powershell -NoProfile -Command "if (Test-Path 'C:\data') { exit 0 } else { exit 2 }"
echo %ERRORLEVEL%
-Fileで実行したスクリプトも、exit <数値>で終了コードを返せます。
実用例:ZIP 圧縮・解凍
Compress-ArchiveとExpand-Archiveを使うと、コマンドプロンプトから ZIP を扱えます。
powershell -NoProfile -Command "Compress-Archive -Path 'C:\data\*' -DestinationPath 'C:\backup\data.zip' -Force"
powershell -NoProfile -Command "Expand-Archive -Path 'C:\backup\data.zip' -DestinationPath 'C:\restore' -Force"
Compress-Archiveの-Forceは既存の ZIP ファイルを、Expand-Archiveの-Forceは解凍先にある同名のファイルを上書きします。
実用例:テキストの置換
ファイル内の文字列を置換する処理は、コマンドプロンプトだけでは難しいですが、PowerShell なら簡単です。
powershell -NoProfile -Command "(Get-Content 'a.txt' -Raw -Encoding UTF8) -replace 'foo','bar' | Set-Content 'b.txt' -Encoding UTF8"
Windows PowerShell 5.1 のSet-Contentは、-Encodingを省略するとシステムの既定コードページ(日本語環境では Shift_JIS 系)で書き出します。文字化けを避けるため、読み込みと書き込みの両方で-Encodingを明示しましょう。なお、5.1 の-Encoding UTF8は BOM 付きで書き出します(PowerShell 7 では BOM なし)。また、バッチファイル自体を UTF-8 で保存していると、-Command内の日本語が文字化けすることがあります。文字コードについては、関連記事も参照してください。
まとめ
| やりたいこと | コマンド |
|---|---|
| PowerShell を対話モードで起動する | powershell |
| 1つのコマンドを実行する | powershell -NoProfile -Command "..." |
.ps1スクリプトを実行する | powershell -NoProfile -ExecutionPolicy Bypass -File a.ps1 |
| PowerShell 7 を使う | pwsh -NoProfile -Command "..." |
| 出力を変数に取り込む | for /f "delims=" %%x in ('powershell ...') do set VAR=%%x |
| 終了コードを受け取る | %ERRORLEVEL% |