mcpField
P
powershell-safe-invocation

powershell-safe-invocation

github.com/misaka-mikoto-tech開発28263 回インストール5 週間前に更新
リポジトリ

powershell-safe-invocation について

ネイティブプログラムの安全な実行やファイル管理が必要な開発者、オートメーション作成者向けの PowerShell ガイドです。引数処理、引用符、文字コード、シェルのバージョン差、実行方法の選択を扱います。

主な機能

  1. 01ネイティブ引数の構造化された受け渡し
  2. 02安全な引用符とスクリプトファイルの指針
  3. 03ハッシュテーブルによる Cmdlet 引数指定
  4. 04文字コードを考慮したファイル処理
  5. 05シェルのバージョンと構文の確認

活用シーン

  1. 01空白を含むパスからのネイティブプログラム起動
  2. 02複数行または引用符が入れ子のスクリプト実行
  3. 03文字コードを保持したファイル処理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/misaka-mikoto-tech/agent-skills --skill powershell-safe-invocation

インストール後、Claude Code で/powershell-safe-invocationを入力して読み込まれたか確認します。

同梱ファイルreference.md

詳細

PowerShell の安全な呼び出し

シェル

Windows PowerShell 5.1 が明示的に必要な場合を除き、pwsh.exe で PowerShell 7 を使用してください。

現在のシェルが不明な場合は、次のコマンドで確認してください。

$PSVersionTable.PSVersion
$PSNativeCommandArgumentPassing

PowerShell 7 をインストールすれば powershell.exe が PowerShell 7 を使用するようになるとは限りません。

  • pwsh.exe = PowerShell 7
  • powershell.exe = Windows PowerShell 5.1

ネイティブプログラム

引数を個別に渡せる場合は、複数の引数をまとめたコマンド文字列を作成しないでください。

次のようにします。

$exe = 'C:\Path With Spaces\tool.exe'
$argList = @(
    '--input'
    'C:\Data Folder\input.json'
    '--flag'
)

& $exe @argList

$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
    throw "$exe failed with exit code $exitCode"
}

ルール:

  • すべてのネイティブ引数を配列の各要素として扱ってください。
  • 変数に格納された実行可能ファイルのパスは、& で呼び出してください。
  • $LASTEXITCODE は直ちに取得してください。
  • $args は PowerShell の自動変数なので、引数配列の名前として使用しないでください。$argList や $nativeArgs などの名前を使用してください。
  • Invoke-Expression は使用しないでください。
  • 実行可能ファイルを起動するだけの目的で、cmd.exe /c のレイヤーを追加しないでください。
  • PowerShell で Bash 形式の \" エスケープを使用しないでください。

コマンドレット

PowerShell のコマンドレットでは、ハッシュテーブルによるスプラッティングを使用してください。

$params = @{
    LiteralPath = 'C:\Data[1]\input.txt'
    Destination = 'C:\Output'
    Force       = $true
    ErrorAction = 'Stop'
}

Copy-Item @params

コマンドレットが対応している場合、具体的なパスには -LiteralPath を使用してください。New-Item のように -Path しか公開していないコマンドレットでは、-Path を使用してください。不明な場合は、Get-Command <cmdlet> -Syntax で確認してください。

PowerShell のコマンドレットの確認に $LASTEXITCODE を使用しないでください。終了エラーを使用してください。

$ErrorActionPreference = 'Stop'

パラメーター値として渡す式は、括弧で囲むか、先に変数に代入してください。-Index 100..120 ではなく、Select-Object -Index (100..120) を使用してください。

foreach (...) { ... } のようなステートメント構文から直接パイプでつながないでください。ステートメントの出力を先に代入するか、パイプラインコマンドレットの ForEach-Object を使用してください。

複雑なコマンド

次のような、引用符の入れ子が深いコマンドは避けてください。

cmd.exe /c pwsh.exe -Command "..."

複数行のコード、引用符の入れ子、JSON、XML、正規表現、パイプライン、リダイレクト、または ASCII 以外のパスを扱う場合は、次のようにします。

  1. 一時的な .ps1 ファイルを書き込みます。
  2. 次のコマンドで実行します。
pwsh.exe -NoLogo -NoProfile -NonInteractive -File script.ps1

短く単純な式を超える場合は、-Command より -File を優先してください。

実行ポリシーが信頼できるスクリプトの実行を実際にブロックしている場合を除き、-ExecutionPolicy Bypass を追加しないでください。

外側の PowerShell プロセスから -Command を使ってスクリプトを渡す必要がある場合、外側のシェルが先に $variables を展開することに注意してください。内側のスクリプトに $p、$env:...、$PSVersionTable などが含まれる場合は、外側のスクリプト文字列を単一引用符で囲んでください。

pwsh.exe -NoLogo -NoProfile -Command '$p = "C:\Data Folder\input.txt"; Test-Path -LiteralPath $p'

コマンドが複数のインタープリターやラッパーを経由する必要がある場合は、そこで止め、引用符の入れ子を増やすのではなく .ps1 ファイルを書き込んでください。

文字列と複数行コード

  • リテラル文字列とパスには、単一引用符を使用してください。
  • PowerShell による展開が必要な場合にのみ、二重引用符を使用してください。
  • バッククォートによる行継続は避けてください。代わりに、配列、ハッシュテーブル、スプラッティング、括弧、またはスクリプトブロックを使用してください。
  • python - <<'PY' のような Bash のヒアドキュメントは使用しないでください。PowerShell では < の解析方法が異なります。一時的なスクリプトファイルを使用するか、PowerShell のヒアストリングをパイプでプログラムに渡してください。
  • JSON にはオブジェクトを作成して ConvertTo-Json を使用し、JSON のエスケープを手作業で行わないでください。
  • リテラルの複数行テキストには、単一引用符のヒアストリングを使用してください。開始の @' の後には何も置かず、終了の '@ は行頭に単独で記述してください。

エンコーディング

PowerShell 7 では、テキスト出力はデフォルトで BOM なしの UTF-8 です。Windows PowerShell 5.1 では、コマンドレットによってデフォルトが異なります。

  • 他のツールが読み込む新しいテキストファイルでは、必要なエンコーディング(通常は utf8)を明示してください。
  • 既存のテキストファイルを変更する前に、既存のエンコーディングを特定し、そのまま維持してください。GBK、UTF-16、または BOM に依存するファイルを、気付かないうちに変換しないでください。エンコーディングが不明な場合は、調査するか確認してください。
  • デフォルトでは Console.InputEncoding や Console.OutputEncoding を設定しないでください。ターミナルまたはネイティブプログラムのエンコーディングに不一致があると確認できた場合にのみ設定してください。PowerShell からネイティブプログラムに送信されるテキストは、代わりに $OutputEncoding で制御します。
  • バイナリファイルにテキストのエンコーディング設定を適用しないでください。バイナリデータにはバイト API を使用してください。

Start-Process

通常のフォアグラウンド実行には、次を使用します。

& $exe @argList

昇格、新規ウィンドウまたは非表示ウィンドウ、プロセスを切り離した起動、あるいはシェルの動作が必要な場合に限り、Start-Process を使用してください。

Start-Process -ArgumentList は値をコマンドライン文字列に結合するため、構造化された引数を確実に渡すための API ではありません。引数の境界を正確に保つ必要がある場合は、ProcessStartInfo.ArgumentList を優先してください。

別プロセスが必要で、引数が複雑な場合は、次を使用します。

$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $exe
$psi.UseShellExecute = $false

foreach ($arg in $argList) {
    $psi.ArgumentList.Add($arg)
}

$process = [System.Diagnostics.Process]::Start($psi)
$process.WaitForExit()

if ($process.ExitCode -ne 0) {
    throw "Process failed with exit code $($process.ExitCode)"
}

ファイル操作

再帰的な削除、移動、上書きの前に、次のことを行ってください。

  • 絶対パスでルートパスと対象パスを解決します。
  • 対象が意図したルート内にあることを確認します。
  • 空のパス、ルート階層のパス、想定外のパスは拒否します。
  • パスを別のシェルに渡さず、ファイルシステムの変更は PowerShell 内で行います。

マップされたドライブは、ユーザー/セッションごとに管理されます。自動化アカウントやサンドボックスアカウントでは Test-Path X:\... が失敗するものの、対話型の実行では成功する場合は、whoami で現在の ID を確認し、Get-PSDrive を調べてください。マッピングが表示されない場合は、ユーザーに UNC パスを確認するか、同じアカウントでマッピングを確立するか、プラットフォームが対応していれば、現在のユーザー/フルアクセスでの実行モードに切り替えてください。

バージョンとモジュールの違い

PowerShell 5.1 と PowerShell 7 では、同じコマンドでも利用できるパラメーターやコマンドの種類が異なる場合があります。バージョン固有のパラメーターを使用する前に、現在のシェルで構文を確認してください。

$PSVersionTable.PSVersion
Get-Command Format-Hex -Syntax
Get-Command Get-FileHash -Syntax

たとえば、Format-Hex -Count は PowerShell 7 では使用できますが、Windows PowerShell 5.1 では使用できません。5.1 では、代わりにパイプラインで出力を制限します。

Format-Hex -LiteralPath 'C:\Data\buffer.bin' | Select-Object -First 2

コマンドの検出動作がおかしい場合は、コマンドレットが存在しないと判断する前に、$env:PSModulePath と Get-Module -ListAvailable <ModuleName> を確認してください。

選択の優先順位

最も簡潔で安全な方法を選択してください。

  1. PowerShell コマンドレット。
  2. & $exe @argList。
  3. pwsh.exe -File を使用した一時的な .ps1 ファイル。
  4. ProcessStartInfo.ArgumentList。
  5. 特殊な動作が必要な場合は Start-Process。
  6. cmd のセマンティクスが必要な場合に限り cmd.exe /c。
  7. Invoke-Expression は、厳密に制御された最終手段としてのみ使用します。

あまり一般的でないケースや完全な例については、reference.md をお読みください。

コメント