PowerShellのStop-Processで自分自身が止まる。exit 255で後続が消えた原因と直し方

運営者

裏で動いている古いスクリプトを1つ止めたくて、部下役のAI(Claude Code)がこの1行を流しました。

powershell -NoProfile -Command "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*_run_build_retry*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force; 'stopped ' + $_.ProcessId }"

返ってきたのは、次の3行だけです。

Exit code 255
stopped 21184
stopped 9072

同じコマンドの後ろには、スクリプトの書き直しと再起動を続けて書いていました。
そのどちらも走っていません。
探していた語が、探している側の命令の中にも入っていたからです。

止めたかったのは、切り離した1本だけだった

2026年10月10日の朝6時44分のことです。
部下役のAIは、声の生成を続けるPowerShellのスクリプトを、Start-Process で切り離して走らせていました。
Start-Process は、新しいプロセス(動いているプログラムの単位)を起動する命令です。
スクリプトを直したので、古い実行を止めてから起動し直すつもりでした。

止める相手は、コマンドライン(起動したときの命令の文字列)で探しました。
Get-CimInstance Win32_Process は、動いているプロセスの一覧を、起動時の命令つきで取り出す命令です。
その中から、スクリプト名の _run_build_retry を含むものを -like で拾い、Stop-Process で止める形でした。

CommandLineには、探している自分の命令も入っている

公式の説明では、Win32_Process の CommandLine は、そのプロセスを開始するために使われたコマンドラインです。
部下役のAIは、Bash(文字で命令を打つ画面の一種)から powershell -Command "…" の形でこの検索を動かしていました。
すると、検索している powershell.exe 自身の CommandLine に、検索語がそのまま入ります。
呼び出し元の bash.exe のコマンドラインにも、同じ文字列が丸ごと入っていました。

-like '*_run_build_retry*' は、自分にも、呼び出し元にも当たります。
4分後に一覧だけを出してみると、bash.exe が3つ、powershell.exe が1つ当たっていました。
Stop-Process は、当たったものを順に止めていきます。
呼び出し元のシェルが止まれば、同じコマンドの後ろに書いた処理は、もう動きません。

画面に後から打った命令は、プロセスを開始したときのコマンドラインには入らないはずです。
この形にはまるのは、別のプログラムから powershell -Command で動かすときだと見ています。
Bash、cmd、タスクスケジューラ、AIの道具などから呼ぶ場合です。
画面に直接打った場合は、試していません。

11月13日に、ダミーで同じ形を作った

記事にするにあたり、止めても困らないダミーで再現しました。
Start-Sleep 300; # psstopdemo_target を実行するだけのPowerShellを、隠して起動します。
そのうえで、Bash から一覧だけを出しました。

22480 bash.exe parent=6932 self=False
8684 bash.exe parent=22480 self=False
21644 powershell.exe parent=14464 self=False
24140 bash.exe parent=8684 self=False
2888 powershell.exe parent=24140 self=True

止めたいダミーは、3行目の21644だけです。
ほかの4つは、検索している自分と、それを呼んだBashでした。
parent は、そのプロセスを起動した親のプロセスの番号です。

同じ条件で Stop-Process まで流すと、ダミーと bash.exe を1つずつ止めたところで、Exit code 255 で終わりました。
後ろに書いた echo は出ていません。
10月10日と同じ止まり方です。

255という数字についても確かめました。
Stop-Process -Id $PID -Force で自分を止めてみました。
PowerShellから見ると、$LASTEXITCODE は -1 でした。
このとき道具の表示は Exit code 255 です。
-1を0から255の範囲で表すと255になるので、その値が出ていると見ています。
公式の説明でも、Stop-Process は .NET の Kill メソッドで実装されているとあります。

直し方1:検索語を文字列の連結で作る

10月10日は、検索語を2つに割ってつなぐ形に直しました。

$pat = '*-File*_run_build_' + 'retry.ps1*'
Get-CimInstance Win32_Process |
  Where-Object { $_.Name -eq 'powershell.exe' -and $_.CommandLine -like $pat } |
  ForEach-Object { Stop-Process -Id $_.ProcessId -Force; 'killed ' + $_.ProcessId }

コマンドラインに残るのは '_run_build_' + 'retry.ps1*' で、つないだ後の文字列は現れません。
だから自分には当たらない、という考え方です。
6時49分と6時50分にこの形で流すと、どちらも後ろの書き直しと再起動まで最後まで走りました。

ただ、連結だけでは足りない場合があります。
11月13日の再現で、ダミーの起動と連結版の検索を、同じBashのコマンドに並べて書いてみました。
するとまた Exit code 255 です。
前半に書いたダミーの起動の部分に、検索語がそのまま入っていたためでした。
呼び出し元のコマンドラインは、1回に流したコマンドの全部です。

10月10日の版が無事だったのは、$_.Name -eq 'powershell.exe' でも絞っていたからでした。
6時50分のコマンドには、再起動のためにスクリプトの名前をそのまま書いていました。
名前で絞っていなければ、ここで呼び出し元のBashに当たっていたはずです。

直し方2:名前で絞って、$PIDを除く

$PID は、いま動いているPowerShell自身のプロセス番号が入る自動変数です。
英語圏のQ&Aでは、$_.ProcessId -ne $PID で自分を除く方法が紹介されています。
11月13日に、組み合わせを変えて試しました。

  • そのまま:ダミーと bash.exe を止めて Exit code 255
  • $PID だけ除く:ダミーと bash.exe を止めて Exit code 255
  • 名前だけで絞る:ダミーを止めたあと自分も止まり、Bashから見た終了コードは127
  • 名前で絞り、$PID も除く:ダミーだけを止め、終了コード0で後ろも実行

$PID で除けるのは、自分のPowerShellだけです。
呼び出し元のBashは名前で外さないと、自分ごと止まります。
いま僕が使っている形はこれです。

Get-CimInstance Win32_Process -Filter "Name='powershell.exe'" |
  Where-Object { $_.CommandLine -like '*myjob.ps1*' -and $_.ProcessId -ne $PID } |
  ForEach-Object { Stop-Process -Id $_.ProcessId -Force; 'stopped ' + $_.ProcessId }

myjob.ps1 のところを、止めたいスクリプトの名前に替えて使います。
PowerShell 7 で動かしている相手なら、名前は pwsh.exe です。
-Filter の中で LIKE を使って絞る場合は、書き方が変わる点に気をつけてください。
公式の説明では、ワイルドカードは * ではなく % で、_ も任意の1文字を表します。
スクリプト名の _ が、別の文字にも当たることになります。

止める前に、一覧だけ出す

止める命令は、先に一覧だけで確かめると安全です。

Get-CimInstance Win32_Process |
  Where-Object { $_.CommandLine -like '*myjob.ps1*' } |
  ForEach-Object { '{0} {1} parent={2} self={3}' -f $_.ProcessId, $_.Name, $_.ParentProcessId, ($_.ProcessId -eq $PID) }

self=True の行があれば、そのまま止めると自分が止まります。
bash.exe や cmd.exe が並んでいれば、呼び出し元も当たっています。
公式の説明にも、プロセスを止めると、それに頼るほかのプロセスまで止まりうると書かれています。

Claude Code の PowerShell の道具から同じ一覧を出したときは、当たったのはダミーだけでした。
道具や呼び方によって、自分が当たるかどうかが変わるということです。
迷ったら、一覧の self と parent を見てから止めています。

参考

6時49分に起動し直したスクリプトは、まだ区切りの行をログに書けていませんでした。
原因は文字コードで、6時50分に先頭へBOMを付けて直しています。
Claude Codeが書いた.ps1が文字化けして動かない(PowerShell 5.1)。先頭にBOMを付けたら直った

切り離したPowerShellでは、OneDriveの中のファイルを消せない日もありました。
OneDrive配下でPowerShellの削除・移動が「アクセスが拒否されました」で止まる。Pythonで移して直した

記事URLをコピーしました