tech.guitarrapc.cóm

C#, PowerShell, Unity, Cloud, Serverless Technical Update and Features

PowerShellでサーバで動いているプロセスを知りたい

さて、今回の刺激元は以下です。
$shibayu36->blog; - サーバで動いているプロセスを知るために使ったコマンド
何かとBashさんとPowerShellさんは比較されるので同じような事をするにはってことで。

Get-ProcessコマンドレットとGet-WmiObject

PowerShellでProcess情報を取得するにあたり、次の2つがすぐに思いつくところです。
Get-Process #Alias = ps, gps
Get-WmiObject win32_process #Alias = gwmi
tasklist #!?

Get-ProcessとGet-WmiObject win32_processでは参照しているオブジェクトが違う

下記の通り、それぞれのコマンドレットは参照オブジェクトが違うため、取得出来る情報(プロパティ)にも差があります。

Get-Process

cmdでいうtasklist、Bashでいうpsのコマンドレット版で、.NETのSystem.Diagnostics.Processオブジェクトを利用しています。

Get-WmiObject win32_process

Windows Management Instrumentation(WMI)に含まれる、Win32_Processオブジェクトを利用しています。

tasklist

ごめんなさいしましょう (汗

取得できる情報(プロパティ)の詳細

Get-Memberコマンドレットでそれぞれを参照していただくと分かります。 まずは、みんな大好きGet-Processから。
#Get-Process | Get-Memberではメソッドも出るためPropertyに制限しています。
Get-Process | Get-Member -MemberType Property
実行結果は、以下です。 (PowerShell V3.0で行っています。)
PS C:\> Get-Process | Get-Member -MemberType Property | select Name,Definition | Format-Table -AutoSize
Name                       Definition
----                       ----------
BasePriority               int BasePriority {get;}
Container                  System.ComponentModel.IContainer Container {get;}
EnableRaisingEvents        bool EnableRaisingEvents {get;set;}
ExitCode                   int ExitCode {get;}
ExitTime                   datetime ExitTime {get;}
Handle                     System.IntPtr Handle {get;}
HandleCount                int HandleCount {get;}
HasExited                  bool HasExited {get;}
Id                         int Id {get;}
MachineName                string MachineName {get;}
MainModule                 System.Diagnostics.ProcessModule MainModule {get;}
MainWindowHandle           System.IntPtr MainWindowHandle {get;}
MainWindowTitle            string MainWindowTitle {get;}
MaxWorkingSet              System.IntPtr MaxWorkingSet {get;set;}
MinWorkingSet              System.IntPtr MinWorkingSet {get;set;}
Modules                    System.Diagnostics.ProcessModuleCollection Modules {get;}
NonpagedSystemMemorySize   int NonpagedSystemMemorySize {get;}
NonpagedSystemMemorySize64 long NonpagedSystemMemorySize64 {get;}
PagedMemorySize            int PagedMemorySize {get;}
PagedMemorySize64          long PagedMemorySize64 {get;}
PagedSystemMemorySize      int PagedSystemMemorySize {get;}
PagedSystemMemorySize64    long PagedSystemMemorySize64 {get;}
PeakPagedMemorySize        int PeakPagedMemorySize {get;}
PeakPagedMemorySize64      long PeakPagedMemorySize64 {get;}
PeakVirtualMemorySize      int PeakVirtualMemorySize {get;}
PeakVirtualMemorySize64    long PeakVirtualMemorySize64 {get;}
PeakWorkingSet             int PeakWorkingSet {get;}
PeakWorkingSet64           long PeakWorkingSet64 {get;}
PriorityBoostEnabled       bool PriorityBoostEnabled {get;set;}
PriorityClass              System.Diagnostics.ProcessPriorityClass PriorityClass {get;set;}
PrivateMemorySize          int PrivateMemorySize {get;}
PrivateMemorySize64        long PrivateMemorySize64 {get;}
PrivilegedProcessorTime    timespan PrivilegedProcessorTime {get;}
ProcessName                string ProcessName {get;}
ProcessorAffinity          System.IntPtr ProcessorAffinity {get;set;}
Responding                 bool Responding {get;}
SessionId                  int SessionId {get;}
Site                       System.ComponentModel.ISite Site {get;set;}
StandardError              System.IO.StreamReader StandardError {get;}
StandardInput              System.IO.StreamWriter StandardInput {get;}
StandardOutput             System.IO.StreamReader StandardOutput {get;}
StartInfo                  System.Diagnostics.ProcessStartInfo StartInfo {get;set;}
StartTime                  datetime StartTime {get;}
SynchronizingObject        System.ComponentModel.ISynchronizeInvoke SynchronizingObject {get;set;}
Threads                    System.Diagnostics.ProcessThreadCollection Threads {get;}
TotalProcessorTime         timespan TotalProcessorTime {get;}
UserProcessorTime          timespan UserProcessorTime {get;}
VirtualMemorySize          int VirtualMemorySize {get;}
VirtualMemorySize64        long VirtualMemorySize64 {get;}
WorkingSet                 int WorkingSet {get;}
WorkingSet64               long WorkingSet64 {get;}
次に、Get-WmiObject win32_processを見てみましょう。
Get-WmiObject win32_process | Get-Member -MemberType Property
実行結果は、以下です。 (PowerShell V3.0で行っています。)
PS C:\> Get-WmiObject win32_process | Get-Member -MemberType Property | select Name, Definition | Format-Table -AutoSize
Name                       Definition
----                       ----------
Caption                    string Caption {get;set;}
CommandLine                string CommandLine {get;set;}
CreationClassName          string CreationClassName {get;set;}
CreationDate               string CreationDate {get;set;}
CSCreationClassName        string CSCreationClassName {get;set;}
CSName                     string CSName {get;set;}
Description                string Description {get;set;}
ExecutablePath             string ExecutablePath {get;set;}
ExecutionState             uint16 ExecutionState {get;set;}
Handle                     string Handle {get;set;}
HandleCount                uint32 HandleCount {get;set;}
InstallDate                string InstallDate {get;set;}
KernelModeTime             uint64 KernelModeTime {get;set;}
MaximumWorkingSetSize      uint32 MaximumWorkingSetSize {get;set;}
MinimumWorkingSetSize      uint32 MinimumWorkingSetSize {get;set;}
Name                       string Name {get;set;}
OSCreationClassName        string OSCreationClassName {get;set;}
OSName                     string OSName {get;set;}
OtherOperationCount        uint64 OtherOperationCount {get;set;}
OtherTransferCount         uint64 OtherTransferCount {get;set;}
PageFaults                 uint32 PageFaults {get;set;}
PageFileUsage              uint32 PageFileUsage {get;set;}
ParentProcessId            uint32 ParentProcessId {get;set;}
PeakPageFileUsage          uint32 PeakPageFileUsage {get;set;}
PeakVirtualSize            uint64 PeakVirtualSize {get;set;}
PeakWorkingSetSize         uint32 PeakWorkingSetSize {get;set;}
Priority                   uint32 Priority {get;set;}
PrivatePageCount           uint64 PrivatePageCount {get;set;}
ProcessId                  uint32 ProcessId {get;set;}
QuotaNonPagedPoolUsage     uint32 QuotaNonPagedPoolUsage {get;set;}
QuotaPagedPoolUsage        uint32 QuotaPagedPoolUsage {get;set;}
QuotaPeakNonPagedPoolUsage uint32 QuotaPeakNonPagedPoolUsage {get;set;}
QuotaPeakPagedPoolUsage    uint32 QuotaPeakPagedPoolUsage {get;set;}
ReadOperationCount         uint64 ReadOperationCount {get;set;}
ReadTransferCount          uint64 ReadTransferCount {get;set;}
SessionId                  uint32 SessionId {get;set;}
Status                     string Status {get;set;}
TerminationDate            string TerminationDate {get;set;}
ThreadCount                uint32 ThreadCount {get;set;}
UserModeTime               uint64 UserModeTime {get;set;}
VirtualSize                uint64 VirtualSize {get;set;}
WindowsVersion             string WindowsVersion {get;set;}
WorkingSetSize             uint64 WorkingSetSize {get;set;}
WriteOperationCount        uint64 WriteOperationCount {get;set;}
WriteTransferCount         uint64 WriteTransferCount {get;set;}
__CLASS                    string __CLASS {get;set;}
__DERIVATION               string[] __DERIVATION {get;set;}
__DYNASTY                  string __DYNASTY {get;set;}
__GENUS                    int __GENUS {get;set;}
__NAMESPACE                string __NAMESPACE {get;set;}
__PATH                     string __PATH {get;set;}
__PROPERTY_COUNT           int __PROPERTY_COUNT {get;set;}
__RELPATH                  string __RELPATH {get;set;}
__SERVER                   string __SERVER {get;set;}
__SUPERCLASS               string __SUPERCLASS {get;set;}

何が違うの?

何が違うか分からない? ですよね~、正直簡単に利用できるのは、Get-Processです。 理由としては以下です。
  1. Get-Processは、デフォルトで表示されるプロパティがあるためTable表示で利用しやすい。(※)
  2. Get-Processの方がコマンド短い!! (psとかgps)
  3. Get-Processは自分のユーザープロセスは制限ないが、他プロセスはUACの昇格が必要
※Get-WmiObjectは、デフォルトをもたないため全て表示しようとするため、Format-Listです。 ※Get-ProcessとGet-WmiObjectでは、Get-WmiObjectではデフォルトの出力が制限されずFormat-Tableでは表示がきれてしまうという…。
2のコマンドが短い = 利用しやすいということで大事です。 少なくとも、Get-WmiObjectでは、表形式で一覧を見ようと思ったときにはSelect-Objectで制限しないと表示もままならない訳で、これは辛いかと思っています。 ただし、両者で、出来ることは似通っています。(当然違いも有りますが) 要は、Select-ObjectとFormat-Tableで指示をすればいいのですが、デフォルトで指示しなくても見れるというのはシェル実行時などには大事な気がします。

お題に挑戦

今回のお題で違いなどを見ていきましょう。 お題のそれぞれに、Get-ProcessとGet-WmiObjectで取り組んで見ます。 必ずUAC権限昇格して行ってください。(権限昇格しないとUser Processの情報しか取得できません)

1. 動いているプロセスを知りたい

Get-Processの場合

対抗心を燃やして……psで…!! ※個人的には、Get-Processを推奨します。
#Get-Processと同一
ps
簡単ですね。タスクマネージャーのような感じででます。(一部違いますが)
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName                                                                      
-------  ------    -----      ----- -----   ------     -- -----------                                                                      
     66       7     1164       1640    64     0.48   2344 ATFSVR26                                                                         
    139      11     1804       2900    89     0.11    540 atieclxx                                                                         
    100       6      800        884    25     0.02    860 atiesrxx                                                                         
    122      13     4436       1340    90     0.94    144 ATOK26IB                                                                         
    115      10     8028       9532    38   148.65   6920 audiodg                                                                          
    149      14     2396       3968    71     0.06   1548 BingDesktopUpdater                                                               
    848      96    92512      10776   888    17.77   5580 CCC                                                                              
    123      11     3864      12500    98     0.08   4720 conhost                                                                          
    496      26    10676       5444   425     0.41   5300 CSISYN~1                                                                         
    430      13     1944       2444    66     3.03    452 csrss                                                                            
    397      25     3452      19680   321   146.58    552 csrss                                                                            
    346      20     5708      10612    81     0.20   1648 dasHost                                                                          
     87       7     1684       3184    34     0.02   3892 dllhost                                                                          
    300      29    36684      61020   390 1,623.00    996 dwm                                                                              
    572      59    16212      45328   208     2.70    436 explorer                                                                         
   1999    1897    99956     475056  1199    93.94   3268 explorer                                                                         

Get-WmiObjectの場合

Get-Processと同じような出力を目指してみましょう。
Get-WmiObject win32_process `
    | select Handles,
        QuotaNonPagedPoolUsage,
        QuotaPagedPoolUsage,
        WorkingSetSize,
        VirtualSize,
        ProcessId,
        Name `
    | sort Name `
    | Format-Table -AutoSize
……長いです。 しかも、CPU %やProcessor Timeが出てません。 ※とはいってもGet-ProcessのCPUもいわゆるタスクマネージャーのようなCPUではなくProcessorTimeなのですが…
Handles QuotaNonPagedPoolUsage QuotaPagedPoolUsage WorkingSetSize VirtualSize ProcessId Name                       
------- ---------------------- ------------------- -------------- ----------- --------- ----                       
     66                      8                 128        1695744    67633152      2344 ATFSVR26.EXE               
    139                     12                 159        3018752    93044736       540 atieclxx.exe               
    100                      7                  41         962560    25735168       860 atiesrxx.exe               
    122                     13                 168        1384448    94855168       144 ATOK26IB.EXE               
    107                     10                  80        9773056    39284736      6920 audiodg.exe                
    143                     14                 118        4059136    74346496      1548 BingDesktopUpdater.exe     
    854                     96                 857        3923968   931344384      5580 CCC.exe                    
    496                     26                 707        5652480   445333504      5300 CSISYN~1.EXE               
    431                     13                 176        2498560    69406720       452 csrss.exe                  
    404                     26                 715       20508672   344805376       552 csrss.exe                  
    346                     20                 153       10895360    85270528      1648 dasHost.exe                
     87                      7                  63        3301376    35373056      3892 dllhost.exe                
    274                     29                 779       64684032   410939392    

動いているプロセスの関係も含めて知りたい

とりあえずどんな感じに実行されているかサマリーを知りたい時は以下のコマンド。

Get-Processの場合

psにFileVersionInfoスイッチを指定するだけです。 ※個人的には、Get-Processを推奨します。
#Get-Process  -FileVersionInfo と同一
ps -FileVersionInfo
上記の-FileVersionInfoスイッチを付けると、参照先がTypeName: System.Diagnostics.FileVersionInfoに変わります。 そのため、取得できるPropertyも変わるのです。 短いProcess名がデフォルトではでないので、selectで指定してあげると見やすくなります。
ps -FileVersionInfo `
    | select productversion, fileVersion, FileName, InternalName
出力例です。
PS C:\> ps -FileVersionInfo | select productversion, fileVersion, FileName, InternalName

ProductVersion                     FileVersion                        FileName                           InternalName                      
--------------                     -----------                        --------                           ------------                      
26.0                               26.0                               C:\Program Files (x86)\JustSyst... ATFSVR26
6.14.11.1126                       6.14.11.1126                       C:\Windows\system32\atieclxx.exe   ATIECLXX.EXE
6.14.11.1126                       6.14.11.1126                       C:\Windows\system32\atiesrxx.exe   ATIESRXX.EXE
26.0                               26.0                               C:\Program Files (x86)\JustSyst... ATOKIB26
Format-Tableでは切れてしまうので、Format-Listの方がいいですね。
PS C:\> ps -FileVersionInfo | select productversion, fileVersion, FileName, InternalName| Format-List

ProductVersion : 26.0
FileVersion    : 26.0
FileName       : C:\Program Files (x86)\JustSystems\ATOK26\ATFSVR26.EXE
InternalName   : ATFSVR26

ProductVersion : 6.14.11.1126
FileVersion    : 6.14.11.1126
FileName       : C:\Windows\system32\atieclxx.exe
InternalName   : ATIECLXX.EXE
残念ながら、Get-ProcessではCommandLineでの実行引数を取れません。 実行引数を参照するには、Get-Processではなくwmiを参照する必要があります。

Get-WmiObjectの場合

上記の実行引数以外はGet-Processの方が楽なのですが… ※CommandLine以外は端折ります>< ソースもとにあった、コマンドの引数とかも表示したい時にwmiを利用します。 Get-WmiObject win32_processには、commandLineプロパティがあるので、ここで実行引数を表示させることができます。
Get-WmiObject win32_process  | select caption, commandLine
出力結果です。(分かり易いsvchostで)
PS C:\Windows\system32> Get-WmiObject win32_process  | select caption, commandLine

caption                     commandLine
-------                     -----------
svchost.exe                 C:\Windows\system32\svchost.exe -k LocalService
svchost.exe                 C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted
atieclxx.exe                atieclxx
svchost.exe                 C:\Windows\system32\svchost.exe -k NetworkService
spoolsv.exe                 C:\Windows\System32\spoolsv.exe
svchost.exe                 C:\Windows\system32\svchost.exe -k LocalServiceNoNetwork
svchost.exe                 C:\Windows\system32\svchost.exe -k apphost

メモリやCPUを消費しているプロセスを知る

はい、これが曲者です。というか、CPU %をイメージすると曲者です。 それ以外は、好きなようにPropertyを指定していただければ… 並び順ですが、基本的には、 | パイプでつないで、sortコマンドレット(Sort-Object)を利用することで、並び替えを指示できます。 -Descendingスイッチで逆順に並びます。(昇順) 取得結果の数制限は、パイプでつないで、selectコマンドレット(Select-Object)で -Firstや-Lastスイッチで頭のいくつという指示ができます。 例:-First 1で最初の一つ

Get-Processの場合

※個人的には、Get-Processを推奨します。 以下のコマンドでvmプロパティの昇順に、最初の10個を指定しています。
#Get-Process | sort vm -Descending | select -First 10 と同一
ps `
    | sort vm -Descending `
    | select -First 10
出力結果です。
PS C:\Windows\system32> ps | sort vm -Descending | select -First 10

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------                                                                      
   2022    3557   109576     134656  1634   213.72   3268 explorer
    762      41    16456      13008  1521            1052 svchost
    878      86   178436     150416  1261    51.40    768 powershell_ise
    575      84   695064     384908  1073 3,848.73   4724 opera
    701      66   164804     172380  1047    24.45   3512 powershell_ise
    998     107   318592     301196  1047 4,319.28   6540 krile
    320    1678    10984      19924   970   263.03    732 taskhostex
    397      32    10348      11000   961            3388 svchost
    879      96    91956       5472   888    28.67   5580 CCC
    230      31    22056      19552   668   368.43   1780 opera_plugin_wrapper
表示順は変わりますが、以下も同じ結果です。 vmプロパティの降順に、最後の10個を指定しています。
#Get-Process | sort vm | select -Last 10 と同一
ps | sort vm | select -Last 10
出力結果です。
PS C:\Windows\system32> ps | sort vm | select -Last 10

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    230      31    22056      19552   668   368.43   1780 opera_plugin_wrapper
    874      96    91920       5512   887    28.67   5580 CCC
    389      32    10224      10932   960            3388 svchost
    323    1680    10984      20268   971   263.03    732 taskhostex
    997     107   317016     299732  1047 4,325.81   6540 krile
    701      66   164804     172380  1047    24.45   3512 powershell_ise
    575      84   687192     377112  1065 3,849.87   4724 opera 
    872      88   181736     158228  1299    57.81    768 powershell_ise
    763      41    16456      13008  1521            1052 svchost
   2041    3563   110040     135560  1638   214.16   3268 explorer                                                               
さて、冒頭で触れたとおり、Get-Processでは、CPU %ではなく、Processor Timeしか取得できません。 そのため、CPUが…という時には見にくいです。

CPU時間ではなく%を取得するには

以下の2つが候補としてあります。
Get-Counter
Get-WmiObject Win32_PerfFormattedData_PerfProc_Process

算出ロジック

中々詳しい情報が、ローマの偉人ググレカス様に伺っても出ないので、msdnと自身で検証を重ねました。 Get-Counterは、論理CPUコア(Core-i7 860の場合 物理コア4のHTなので8です)でCookedValueを割ることで、各プロセス%が算出できます。 一方の、Get-WmiObject Win32_PerfFormattedData_PerfProc_Processは、物理CPUコア((Core-i7 860の場合 物理コア4なので4)でPercentProcessorTimeを割ることで、各プロセス%が算出できます。 ただし、検証している中で、Get-WmiObjectは、数値が実行毎にばらつきが出るようです。(謎 / 某Script Guyさんも同じような指摘を…) 一方のGet-Counterは、比較的安定しているようなので、個人的にはGet-Counterが望ましいのかと判断しています。 さっくり、CPU %を取得するコマンドを書いてみました。

Get-Counterの場合

環境変数のNUMBER_OF_PROCESSORSでCookedValueを割って、それを[int]型に型変換することで小数点を取り払っています。 ($env:NUMBER_OF_PROCESSORSで環境変数の論理CPUコアにアクセスできます) 今回の場合、論理CPU =8で割っています。
(Get-Counter "\Process(*)\% Processor Time").CounterSamples `
    | select InstanceName, 
        @{label="CPU %";expression={$_.CookedValue / $Env:NUMBER_OF_PROCESSORS -as [int]}} `
    | where {$_.InstanceName -ne "idle" -and $_.InstanceName -ne "_total"} `
    | sort "CPU %" -Descending `
    | select -First 10 `
    | Format-Table -AutoSize
出力結果です。
InstanceName          CPU %
------------          -----
iexplore                 28
iexplore                  5
justonlineupdate          0
ccc                       0
jslmaui                   0
mom                       0
csisyn~1                  0
everything-1.2.1.421b     0
magicformation            0
skydrive                  0

Get-WmiObjectの場合

ロジックは同じです。 環境変数のNUMBER_OF_PROCESSORSでPercentProcessorTimeを割って、それを[int]型に型変換することで小数点を取り払っています。 ($env:NUMBER_OF_PROCESSORSにある環境変数の論理CPUコアを/2してHTを除く物理CPU数としています。) 今回の場合、物理CPU = 4で割っています。
Get-WMIObject -Query "Select * From Win32_PerfFormattedData_PerfProc_Process Where PercentProcessorTime >= 0" `
    | where {$_.Name -ne "_Total" -and $_.Name -ne "Idle"} `
    | sort PercentProcessorTime -Descending `
    | select -First 10 `
        CreatingProcessID,
        Name,
        PercentProcessorTime,
        @{label="CPU / Core";expression = {$_.PercentProcessorTime / ($env:NUMBER_OF_PROCESSORS / 2 )}},
    | Format-Table
出力結果です。

CreatingProcessID Name           PercentProcessorTime CPU / Core
----------------- ----           -------------------- ----------
             1632 iexplore                         24          6
             3268 powershell_ise                   12          3
              640 svchost#7                         0          0
              640 svchost#6                         0          0
              640 svchost#5                         0          0
              640 svchost#4                         0          0
              640 svchost#3                         0          0
              640 svchost#2                         0          0
              640 svchost#1                         0          0
              640 svchost                           0          0
同時実行でないので、Gt-CounterとGet-WmiObjectには差異があります。 例え同時実行しても結果が違うことがままったのですが……。 困ったものです…が、ここではタスクマネージャーとにらめっこして近かったのがGet-Coutnerでした。 よって、Get-Counterを採用します。(海外フォーラムでも、比較してこの結論に至ったところがありましたが……さて)

まとめ と おまけ

まとめ

さて、元ソースの内容は、PowerShellでも簡単に見れることが分かるかと思います。 Get-WmiObjectを利用すると、とても冗長ですが、Get-Processを使えば簡単ですね。 Propertyの指定に、幾分冗長的な部分はありますが……ps | gmなどで確認してください。※ ※Get-Process | Get-MemberのAliasです。

おまけ

Get-ProcessとGet-WmiObject win32_processにGet-Counterで得たCPU %を組み込んでみました。 function化はさせていないのですが、ここまで読まれた方には不要かと…。 参考程度にどうぞ。

Get-ProcessにGet-CouterのCPU%をプロパティとして追加する

$PerfCounter = (Get-Counter "\Process(*)\% Processor Time").CounterSamples `
    | select InstanceName, 
        @{label="CPU %";expression={($_.CookedValue / $Env:NUMBER_OF_PROCESSORS) -as [int]}} `
    | where {$_.InstanceName -ne "idle"} `
    | where {$_.InstanceName -ne "_total"}

Get-Process `
    | ForEach-Object {

        #Join to obtain Get-Counter "CPU %" matching by InstanceName and ProcessName
        $NameStr = $_.ProcessName
        $processCPU = $PerfCounter | where{$_.InstanceName -match $NameStr} | select "CPU %" | sort -Unique
    
        #Format CPU(s)
        $CPU = "{0:0.00}" -F $_.CPU

        #Create new Property to show all in one
        New-Object PSObject -Property @{ 
        Handles = $_.Handles
        "NPM(K)" = [int]($_.NonpagedSystemMemorySize / 1KB )
        "PM(K)" = [int]($_.PagedMemorySize / 1KB)
        "WS(K)" = [int]($_.WorkingSet / 1KB)
        "VM(M)" = [int]($_.VirtualMemorySize / 1MB)
        "CPU(s)" = $CPU
        "PerfCPU %" = $processCPU.'CPU %'
        Id = $_.Id
        ProcessName = $_.ProcessName

        }
    } `
    | sort "PerfCPU %" -Descending `
    | Format-Table Handles,"NPM(K)","PM(K)","WS(K)","VM(M)","CPU(s)","PerfCPU %",Id,ProcessName -AutoSize

出力結果(抜粋)です。
Handles NPM(K)  PM(K)  WS(K) VM(M) CPU(s)  PerfCPU %   Id ProcessName
------- ------  -----  ----- ----- ------  ---------   -- -----------
    851     81 174484 216312  1146 4.62            2 8184 powershell_ise
    607     59 112424 103364   424 78.25           2 5232 GOM
   1112    118 150296 173608   593 900.02          1 6400 iexplore
    629     46  55076   7532   415 160.93          1 5820 iexplore
    515     30  12248   4572   179 3.18            1 5784 iexplore
    629     47  16720  29316   294 14.84           1 1632 iexplore
    974    118 104024 118452   569 3297.99         1 3184 iexplore
    147     12   2024   2012    71 0.06            0 2392 vmware-usbarbitrator64 

Get-WmiObjectにGet-CouterのCPU%をプロパティとして追加する

$PerfCounter = (Get-Counter "\Process(*)\% Processor Time").CounterSamples `
    | select InstanceName, 
        @{label="CPU %";expression={($_.CookedValue / $Env:NUMBER_OF_PROCESSORS) -as [int]}} `
    | where {$_.InstanceName -ne "idle"} `
    | where {$_.InstanceName -ne "_total"}

Get-WmiObject win32_process `
    | ForEach-Object {

        $Nametemp = $_.Name
        $NameStr = $Nametemp.Substring(0,$Nametemp.Length-4)

        $processName = $PerfCounter.InstanceName | where{$_ -match $NameStr} | select -Unique
        $processCPU = $PerfCounter | where{$_.InstanceName -match $NameStr} | select "CPU %" | sort -Unique

        New-Object PSObject -Property @{ 
        Handles = $_.Handles
        "NPM(K)" = $_.QuotaNonPagedPoolUsage
        "PM(K)" = $_.QuotaPagedPoolUsage
        "WS(K)" = [int]($_.WorkingSetSize / 1KB)
        "VM(M)" = [int]($_.VirtualSize / 1MB)
        "PerfCPU %" = $processCPU.'CPU %'
        ProcessId = $_.ProcessId
        ProcessName = $_.Name

        }
    } `
    | sort "PerfCPU %" -Descending `
    | Format-Table Handles,"NPM(K)","PM(K)","WS(K)","VM(M)","PerfCPU %",ProcessId,ProcessName -AutoSize
出力結果(抜粋)です。
Handles NPM(K) PM(K)  WS(K) VM(M) PerfCPU % ProcessId ProcessName                
------- ------ -----  ----- ----- --------- --------- -----------
    607     59   523 103300   424         1      5232 GOM.exe
    396     32   108  10948   960         0      3388 svchost.exe
    886     97   862   1884   892         0      5580 CCC.exe
     65      9   140   1264    71         0      5532 JSLMAUI.exe
    300     34   373   1672   588         0      5500 MOM.exe
    496     26   707   5268   425         0      5300 CSISYN~1.EXE

簡単な説明

Get-Counterでは、ProcessNameしかとれず、ProcessIdが取れません。 そこで、Get-ProcessやGet-WmiObjectで取得したProcessNameと、Get-Counterで取得したProcessNameの合致を確認してそのProcessのCPU %としてGet-Coutnerの取得結果を紐づけています。 これをGet-Processから ForEach-Objectでつないだ{}内で各Processに対して行っています。 最後に、紐づけたCPU %をGet-ProcessやGet-WiObjectで、一覧結果表示できるように新しいPropertyを追加しています。 New-Object -Propertyは、汎用的に利用できる手段なので非常に有用ですね。