PowerShell script for process information on one line, like Date and time, pid, cpu usage %, mem usage MB and the command line info like Task Manager

110 views Asked by At

Can you help me write a PowerShell script for process information?

I would like the output to be in the format of: 20240103 21:29 - pid: 9640 - cpu: 0.1 % - mem: 50 MB - C:\Windows\notepad.exe test1.txt

I have come up with this so far:

$app="notepad"
$ids=(Get-Process $app | Select-Object -Property Id) | ForEach-Object {$_.Id}
$date=(Get-Date -format "yyyy-MM-dd HH:mm")

#echo $pids

foreach ($id in $ids) {
 
  # process CPU information
  $name = Get-CimInstance -ClassName Win32_PerfRawData_PerfProc_Process -Filter "IDProcess = $id" | Select-Object -ExpandProperty Name
  $rawusage = (Get-Counter -Counter "\Process($name)\% Processor Time").CounterSamples.CookedValue
  $usage = "{0:F2}" -f $rawusage

  # process Mem information
  # to be completed

  # commandline information
  $commandline=((Get-CimInstance Win32_Process -Filter "ProcessId=$id").CommandLine)
  
  echo "$date - pid: $id - cpu: $usage % - command: $commandline"

  # write to file
  # to be completed
}

Output is:

2024-01-03 23:46 - pid: 2144 - cpu: 0,00 % - command: "C:\Windows\system32\notepad.exe" .\test2.txt
2024-01-03 23:46 - pid: 5796 - cpu: 0,00 % - command: "C:\Windows\notepad.exe" .\test..txt
2024-01-03 23:46 - pid: 7736 - cpu: 0,00 % - command: "C:\Windows\system32\notepad.exe" .\test3.txt
2024-01-03 23:46 - pid: 8156 - cpu: 0,00 % - command: "C:\Windows\system32\notepad.exe" .\test1.txt

It has been a stuggle especially the cpu info.

Can you help me to even more simplify this? Or help me with the mem info?

I have been using Google and ChatGPT. I hope you can help with your expertise.

2

There are 2 answers

0
sirtao On

The main issue is the CPU %: CPU usage is a relative value, updated every millisecond I think, so if you are just looking to make a "Task manager in Powershell" I suggest you to just use NTop(it's also available on Winget ), otherwise I have not found a reliable way to get that info. So I did skip it.

That said: this will return an array with all the info you requested, EXCEPT CPU %, so you can manipulate and format it better to fit your needs (if you find a way to get CPU% you can just add it to the [PSCustomObject])

Get-Process -Name $ProcessName | ForEach-Object {
    [pscustomobject]@{
        Timestamp   = Get-Date -Format 'yyyy-MM-dd HH:mm'
        ProcessId   = $_.Id
        MemoryInMB  = $_.PrivateMemorySize64 / 1MB
        CommandLine = ( Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$($_.id)").CommandLine
    }
0
Benjamin Crouch On

There are two important things to consider here.

The first, is that notepad uses almost no CPU unless you're really working it hard. In my testing, I had to copy and paste thousands of lines very rapidly for the CPU use of notepad to be higher than 0%. While you're testing your script, you might want to try using a different process for testing that you know will be using a good amount of CPU at all times. Don't forget that 0.00 is a valid option!

The second is that when measuring CPU information this way, you're measuring the % utilization of a single logical core. So on a machine with 4 logical cores (such as a two core machine with hyperthreading) if you see a process using exactly 25% of the CPU in task manager, your PowerShell will tell you the CPU utilization of the process is 100%! That's because it's using 100% of a single logical core, which is 1/4 of your total logical cores, which is 25% of your totally CPU utilization.

If your process is using 50% of your CPU according to task manager, then you'll measure 200% CPU utilization when you measure it directly.

Task manager measures the raw CPU just like you do, but it translates the raw CPU utilization into the percent of total processing available to the machine. It makes it easier for people to consume that metric. If you'd like to do the same, then you need to divide the $rawusage variable by the total number of logical cores.

I've got an example of how you could do this with your code below.

$app="notepad"
$ids=(Get-Process $app | Select-Object -Property Id) | ForEach-Object {$_.Id}
$date=(Get-Date -format "yyyy-MM-dd HH:mm")

### Count the number of cores you have ###
$cores = (Get-wmiobject -class win32_processor).numberoflogicalprocessors

#echo $pids

foreach ($id in $ids) {
 
  # process CPU information
  $name = Get-CimInstance -ClassName Win32_PerfRawData_PerfProc_Process -Filter "IDProcess = $id" | Select-Object -ExpandProperty Name
  $rawusage = (Get-Counter -Counter "\Process($name)\% Processor Time").CounterSamples.CookedValue

### Divide raw usage by the number of logical cores to see total CPU used ###
### Also, your desired output only had 1 decimal place. So we'll fix that here too ###
  $usage = "{0:F1}" -f ($rawusage / $cores)

  # process Mem information
  # to be completed

  # commandline information
  $commandline=((Get-CimInstance Win32_Process -Filter "ProcessId=$id").CommandLine)
  
  echo "$date - pid: $id - cpu: $usage % - command: $commandline"

  # write to file
  # to be completed
}

Here's a link to a good stack overflow post about measuring memory of a process with PowerShell. --> Get-Process with total memory usage