しばたテックブログ

気分で書いている技術ブログです。

PowerShellで外部プロセスのコマンドラインを取得する

何気に.NET Framework/.NET Coreには外部プロセスのコマンドラインを取得する方法が無く、このため、PowerShellからコマンドラインを取得しようとする場合少し手間をかけてやる必要があります。

PowerShellで外部プロセスのコマンドラインを取得する

簡単な関数Get-ProcessCommandlineを作ってGistに上げました。
使用例と併せてご覧ください。

厳密に対応バージョンを考慮していませんが、Windows PowerShell、PowerShell Core両方の大抵の環境で動くはずです。

gist.github.com

簡単な解説

各プラットフォーム毎の簡単な解説を補足しておきます。

Windowsの場合

Windowsにおいて外部プロセスのコマンドラインを取得するには、ReadProcessMemoryなどのプロセス情報を読み取るWin32 APIを使うかWMIを使う必要があります。

PowerShellからであればWMIを扱うほうが圧倒的に楽なので先述の関数でもWMIを使っています。
Win32_ProcessクラスにCommandLineというズバリなプロパティがあるのでこれを取得するだけでOKです。

# Windowsでは Win32_Process クラスの CommandLine プロパティからコマンドラインを取得可能
$proc = Get-WmiObject -Class Win32_Process -Filter "ProcessId = $Id" -Property "ProcessId", "CommandLine"
if ($null -eq $proc) {
    return ""
}
return $proc.CommandLine

# PowerShell Core なら Get-CimInstance を使う
$proc = Get-CimInstance -Class Win32_Process -Filter "ProcessId = $Id" -Property "ProcessId", "CommandLine"
if ($null -eq $proc) {
    return ""
}
return $proc.CommandLine

Linuxの場合

Linuxの場合プロセスの情報を/proc/から取得することができます。
プロセスのコマンドラインは/proc/[プロセスID]/cmdlineに記述されているのでGet-Contentで内容を読み取ってやればOKです。

注意すべきところはコマンドラインのデリミタがヌル文字(\0)である点くらいでしょうか。

# Linuxでは /proc/[プロセスID]/cmdline からコマンドラインを取得可能
if (-not (Test-Path -LiteralPath "/proc/$Id")) {
    return ""
}
return @(Get-Content -LiteralPath "/proc/$Id/cmdline")[0] -replace "\0", " "

macOSの場合

残念ながらmacOSには/proc/が存在せずLinuxと同じ様にはいきません。
macOSのシステムプログラミングには全く詳しくないため大した調査は出来なかったのですが、

stackoverflow.com

を見る限りではCで頑張らないとダメな様です。

ちょっと本末転倒な感じもありますが、PowerShellからだとpsコマンドの結果を抜くのが一番手っ取り早く確実です。
(これならはじめからpsコマンドだけで良いのでは?というお気持ちです...)

psコマンドで特定の要素だけ取得するには-oオプションを、取得結果からカラムヘッダーを除外するには=をつけてやればよいため、ps -o commnand=の様に指定してやればコマンドラインを取得できます。

# macOSの場合は psコマンドを使ってコマンドラインを取得可能
return (/bin/ps -o command= -p $Id)