はじめに
Windowsでファイルやデータをバッチ処理したいとき bat ファイルだと構文が複雑だし ps1 ファイルだと実行ポリシーの設定が面倒ということがありました。
そこで、bat ファイル内で PowerShell を呼び出すことで、実行ポリシーの設定なしに処理が可能になるため、使い方についてまとめます。
使用環境・使用ツール
- Windows11
- コマンドプロンプト
基本的な書き方
powershell のコマンドを使用するときは powershell -Command を記載する必要があります。
@echo off はバッチファイルの実行時にコマンドを画面表示しないための設定です。
- シンプルな例
@echo off
powershell -Command "Get-Process | Select-Object Name, CPU"
pause
powershell -Command 以降は、文字列として保護する
-
複数行の例
複数行に分けて記載する場合は、行末に半角スペースとキャレット^を記載する必要があります。
@echo off
powershell -Command ^
"$files = Get-ChildItem *.txt; " ^
"foreach($file in $files) { " ^
" Write-Host $file.Name " ^
"}"
pause
注意点
^ の直後に改行する(スペースが入ると動作しない)
コマンドプロンプト上で引数を指定した場合、%数字 で取得することができます。
@echo off
set INPUT_FILE=%1
powershell -Command ^
"$content = Get-Content '%INPUT_FILE%'; " ^
"Write-Host \"行数: $($content.Count)\""
呼び出すときは '%変数名%'と記載する。
実行ポリシーについて
PowerShell には実行ポリシーというセキュリティ機能があります。
# PowerShellで確認
Get-ExecutionPolicy
# 結果例: Restricted(制限あり)
実行ポリシーの種類
- Restricted
スクリプト実行不可(デフォルト) - RemoteSigned
ダウンロードしたスクリプトのみ署名必要 - Unrestricted
全て実行可能
よくある問題
実行ポリシーが Restricted だった場合、エラーが発生して実行できません。
# .ps1ファイルを直接実行
PS> .\script.ps1
# エラー: このシステムではスクリプトの実行が無効になっています
実行可能にするには、設定方法を変更する必要があります。(管理者権限必要)
# 管理者としてPowerShell起動後
Set-ExecutionPolicy RemoteSigned
bat ファイル経由だと実行ポリシーが関係ないのか?
PowerShell の実行ポリシーは「.ps1ファイルの直接実行」のみをチェックするようです。
# 実行ポリシーチェック対象(.ps1ファイル実行)
powershell -File script.ps1
# 実行ポリシーチェック対象外(文字列コマンド実行)
powershell -Command "Get-Process"
-
.ps1ファイル⇨ セキュリティチェックあり - 文字列コマンド ⇨ セキュリティチェックなし
batファイル内の -Command は文字列として扱われるため、実行ポリシーの制限を受けません
実際の比較例
:: .ps1ファイルの実行(エラーになる可能性)
powershell -File get_files.ps1
:: batファイル内に文字列コマンドを記述(問題なし)
powershell -Command "Get-ChildItem *.txt"
これにより、管理者権限なしで複雑なPowerShell処理が実行可能になります。
実践的な例
複数ファイルの情報を CSV で出力する方法
@echo off
set OUTPUT_FILE=result.csv
powershell -Command ^
"$files = Get-ChildItem *.txt; " ^
"$results = @('ファイル名,サイズ,更新日'); " ^
"foreach($file in $files) { " ^
" $line = $file.Name + ',' + $file.Length + ',' + $file.LastWriteTime; " ^
" $results += $line " ^
"}; " ^
"$results | Out-File '%OUTPUT_FILE%' -Encoding UTF8"
echo 処理完了: %OUTPUT_FILE%
pause
バイナリファイルから特定データを取得する方法
@echo off
set INPUT_FILE=%1
set OUTPUT_FILE=extracted_data.csv
powershell -Command ^
"$bytes = [System.IO.File]::ReadAllBytes('%INPUT_FILE%'); " ^
"$encoding = [System.Text.Encoding]::GetEncoding('Shift_JIS'); " ^
"$results = @(); " ^
"for($i = 0; $i -lt $bytes.Length; $i += 10) { " ^
" $end = [Math]::Min($i + 9, $bytes.Length - 1); " ^
" $text = $encoding.GetString($bytes[$i..$end]).Trim(); " ^
" if($text -ne '') { $results += $text } " ^
"}; " ^
"$results | Out-File '%OUTPUT_FILE%' -Encoding UTF8"
echo 抽出完了
pause
PowerShellの自動変数について
$_ はPowerShellの自動変数で、「現在処理中のオブジェクト」を表します。
ファイルの各行を空白で分割
:: Get-Contentがファイルの各行を読み込む
:: `|`(パイプ)で次のコマンドに渡す
:: ForEach-Objectで1行ずつ処理
:: `$_` は「現在処理中の行」を表す
"Get-Content '%1' | ForEach-Object { $_ -split '\s+' }"
よくあるトラブルと解決法
- 日本語文字化け
:: Shift_JIS で読み込む
"$encoding = [System.Text.Encoding]::GetEncoding('Shift_JIS'); " ^
"$text = $encoding.GetString($bytes)"
- パスにスペースが含まれる
:: シングルクォートで囲む
"$file = Get-Content 'C:\Program Files\data.txt'"
- 引用符のエスケープ
:: バッククォートでエスケープ
"$text = `"Hello World`""
メリット・デメリット
batファイル内で PowerShell を使うことで以下のメリット・デメリットがあります。
メリット
- 実行ポリシー設定不要
- ファイル・データ処理が簡単に実行可能
- 既存のbatファイルに機能追加しやすい
デメリット
- コードが長くなると可読性が悪化し、デバッグが困難
- 毎回PowerShellプロセス起動のオーバーヘッドあり
- 構文ハイライト、デバッガー等のサポート機能が使えない
補足(ラッパーbat方式によるデメリット対策)
前述のデメリットを解決する方法として、ラッパーbat方式があるそうです。
方式
PowerShellスクリプト(.ps1)を作成し、bat ファイルから呼び出します。
メリット
- 実行ポリシーを一時的に回避できる
- PowerShellの機能が使える(構文ハイライト、デバッガー等)
- 可読性・保守性が向上
デメリット
- ファイルが bat と ps1 で2つ必要
複雑な処理や定期的な保守が必要な場合は、この方式も検討してみてください。
まとめ
大量ファイルの処理や、バイナリデータの操作では bat の構文よりも比較的理解しやすいので、おすすめです。
参考