PowerShell在Linux和OSX中获取进程的命令行参数

问题描述

在PowerShell中,使用Get-WmiObject win32_process可以获取Windows(like this)中任何活动进程的命令行参数。在Linux和/或OSX中您如何做?

可以肯定,ps命令可以做到这一点,但是我正在寻找PowerShell本地解决方案,以便能够保持强类型。

解决方法

注意:从PowerShell Core 7.1.0-rc.2开始,存在一个新的7.1 .CommandLine ETS脚本属性,该属性确定Windows上的命令行和Linux上的 。 但是:

  • 在撰写本文时,GitHub feature request #13943尚未提供针对 macOS 的解决方案。
  • Linux {em> 解决方案的行为有些出乎意料,如GitHub issue #13944 中所述-以下解决方案可以解决此问题,尽管以失去参数边界为代价。

底部详细介绍的解决方案可在macOS上运行(至少在某些Linux发行版上也是如此),并且可以轻松地集成到.CommandLine属性中,如下所示-您甚至可以在早期的PowerShell [Core]中执行此操作]版本,甚至在 Windows PowerShell 中:

# Run this once per session to add a / update the .CommandLine property
# on System.Diagnostics.Process to report the process' command line
# on Windows,Linux,and macOS
Update-TypeData -Force -Value {
  if ($env:OS -eq 'Windows_NT') {
    (Get-CimInstance Win32_Process -Filter "ProcessId = $($this.Id)").CommandLine
  } elseif ($IsLinux) {
    (Get-Content -LiteralPath "/proc/$($this.Id)/cmdline") -replace "`0",' '
  } elseif ($IsMacOs) {
    ps -o command= $this.Id
  }
} -TypeName System.Diagnostics.Process -MemberName CommandLine -MemberType ScriptProperty 

启用后,您可以按以下方式使用它:

$pidOfInterest = $PID # use the PowerShell session's own as an example
(Get-Process -Id $pidOfInterest).CommandLine

注意:

  • 与Windows不同,在Unix平台上,报告的命令行不是原始命令行的忠实表示;值得注意的是,最初由单引号或双引号强制执行的参数边界已丢失

    • 注意:在类Unix平台上,进程没有收到它们自己必须解析的命令行(不幸的是,在Windows上是这样);相反,他们收到了 verbatim 参数的 array 。因此,没有这样的原始命令行,只有传递给原始 shell 的特定于Shell的命令行,然后将其转换为在创建时传递给进程的逐字参数数组。

    • 在macOS上,下面详细介绍的基于ps的解决方案提供有关原始参数边界的 no 信息,并仅将逐字参数与空格连接在一起,因此生成的命令行通常不能期望字符串像原始调用一样工作。

    • 在Linux上,特殊的/proc/<pid>/cmdline文件 do 仅使用NUL个字符(0x0)而不是空格来保留原始参数边界。加入逐字辩论;但是,要从中重建一个有效的命令行将需要不懈的努力(特别是需要根据需要重新创建引号和转义符),甚至提出了一个概念上的挑战:您使用什么shell 命令行,例如,假设PowerShell的语法不同于Bash的语法?因此,上面的命令还将Linux上的逐字参数简单地与空格连接起来,就像ps在macOS上一样。

  • 访问.CommandLine属性在性能上是昂贵的,因为它在Windows上调用CIM / WMI调用,并在Unix的子进程中运行外部可执行文件。


在macOS上,您可以按以下方式使用标准ps utility [1] ,但请注意,它返回的命令行缺少原始引号:

# Print the command line used to invoke this PowerShell session,# represented by automatic variable $PID.
# Note that $PID is just an *example* PID (process identifier).
ps -o command= $PID

注意:commandargs字段名称的非标准变体,不受后者的64个字符的限制。 Linux和macOS。


为说明报价问题:

以下命令(从PowerShell执行):

pwsh -noprofile -noexit -c "'hi there'; ps -o command= `$PID"

输出一个字符串,例如:

/usr/local/bin/pwsh -noprofile -noexit -c 'hi there'; ps -o command= $PID

请注意,包含原始"..."参数的-c引号是如何丢失的。


[1] 特定于 Linux的替代项是Get-Content -LiteralPath /proc/$PID/cmdline ,它具有使用NUL分隔参数的优点。字符以保留原始参数边界-但要查看这些边界,需要额外的工作。 ps解决方案也可以在至少某些Linux发行版上使用,但是可以想象,正在使用的ps实现不支持command字段。无论哪种方式,ps解决方案都缺少有关原始参数边界的信息。