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

PowerShell get Command Line Args of Processes in Linux and OSX

在 PowerShell 中,使用 Get-WmiObject win32_process 可以获得 windows (like this) 中任何活动进程的命令行参数。你如何在 Linux and/or OSX 中做同样的事情?

ps 命令确实可以做到这一点,但我正在寻找 PowerShell 原生解决方案,以便能够保持强类型。

注意:从 PowerShell Core 7.1.0-rc.2 开始,new-for-7.1 .CommandLine ETS 脚本 属性 存在,用于确定 [=139= 上的命令行] 和 在 Linux 上。 然而:

  • 在撰写本文时未提供针对 macOS 的解决方案,GitHub feature request #13943 旨在更正。
  • Linux 解决方案的行为有些出乎意料,如 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 平台上,报告的命令行 不是 原始命令行的忠实表示;值得注意的是,最初由单或 double-quoting 强制执行的参数边界丢失了:

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

    • 在 macOS 上,下面详述的基于 ps 的解决方案提供了 no 关于原始参数边界的信息,并简单地将逐字参数与空格连接起来,因此生成的 command-line 字符串通常不能像原始调用那样工作。

    • 在 Linux 上,特殊 /proc/<pid>/cmdline 文件 do 保留原始参数边界,只需使用 NUL字符(0x0)而不是空格来加入逐字的参数;然而,从中重建一个可用的命令行需要付出不小的努力(特别是需要重新创建必要的引用和转义),甚至这也提出了概念上的挑战:what shell 你是否重建命令行,例如,考虑到 PowerShell 的语法不同于 Bash 的语法?因此,上面的命令也只是将 Linux 上的逐字参数与空格连接起来,就像 ps 在 macOS 上所做的一样。

  • 访问 .CommandLine 属性 在性能方面代价高昂,因为它调用 CIM/WMI 对 Windows 的调用,并且 运行 Unix 子进程中的外部可执行文件。


在macOS上,可以使用标准的ps utility[1]如下,但是要注意命令行它 returns 缺少原始引用:

# 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-specific 替代方案是 Get-Content -LiteralPath /proc/$PID/cmdline,它具有用 NUL 字符分隔参数的优点是为了保留原始参数边界 - 但要查看这些边界需要额外的工作。 ps 解决方案也适用于至少一些 Linux 发行版,但可以想象有 ps 正在使用的实现不支持 command 字段。无论哪种方式,ps 解决方案都缺少有关原始参数边界的信息。