1

I'm hoping I can get a helping hand here. I love linux shell scripting and I decided to give Windows Powershell a try for the following challenge and I'm failing miserably.

I want to run the following command which outputs the GPU temperature and CPU utilization as the 4th and 5th columns. If the 4th column falls below 50, or the 5th column falls below 80 I want to reboot my system.

"C:\Program Files\NVIDIA Corporation\NVSMI>nvidia-smi" --query-gpu=timestamp,name,pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory --format=csv -l 5

Sample of what the command prints every 5 seconds:

2018/05/21 21:21:54.118, GeForce GTX 1060 6GB, 00000000:01:00.0, 63, 100 %, 93 %
2018/05/21 21:21:59.121, GeForce GTX 1060 6GB, 00000000:01:00.0, 64, 100 %, 95 %
2018/05/21 21:22:04.122, GeForce GTX 1060 6GB, 00000000:01:00.0, 64, 100 %, 94 %

I can easily do this with bash, how would I approach this with Powershell? I'm hoping learning this will get me to understand Powershell. Thanks.

codepoor
  • 21
  • 4

2 Answers2

1

I'm starting to get the hang of this. It's still magic to me that Powershell knows the actual names for the output. How is it doing that? Is it looking at the parameters I passed the command?

Here's my new code, which stops the ethminer process and restarts it if the temp is less than 50. The problem is that once it restarts ethminer I have it wait 120 seconds, but the next iteration of the loop is the temperature from 120 seconds ago. Do i have to break the loop or is there a cool Powershell way of telling it to use the last output?

$cmd = "& 'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi' --query-gpu=timestamp,name,pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory --format=csv -l 5"

invoke-expression $cmd | ConvertFrom-CSV | ForEach-Object {
  $_
  if ([int]$_.'temperature.gpu' -lt 50) {
    "temp $($_.'temperature.gpu') is less than 50 restarting ethminer"
    $process = Get-Process -Name "ethminer"
    Stop-Process -InputObject $process
    Get-Process | Where-Object {$_.HasExited}
    Start-Process "C:\Users\svill\Desktop\start - gpu 1.bat.lnk"
    Start-Sleep -s 120
  }
}

Update: Here is a working version of the code running the command over and over with 1 line of output:

$cmd = "& 'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi' --query-gpu=timestamp,name,pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory --format=csv"

while($true)
{
  invoke-expression $cmd | ConvertFrom-CSV | ForEach-Object {
    $_
    if ([int]$_.'temperature.gpu' -lt 45) {
      "temp $($_.'temperature.gpu') is less than 45 restarting ethminer"
      $process = Get-Process -Name "ethminer"
      Stop-Process -InputObject $process
      Get-Process | Where-Object {$_.HasExited}
      Start-Process "C:\Users\svill\Desktop\start - gpu 1.bat.lnk"
      Start-Sleep -s 60
    }
    Start-Sleep -s 5
  }
}
codepoor
  • 21
  • 4
  • Select-Object -Last 1 from https://stackoverflow.com/questions/4546567/get-last-element-of-pipeline-in-powershell – lit May 22 '18 at 12:35
  • Alternatively, can `nvidia-smi` be told to only output one (1) line? – lit May 22 '18 at 12:41
  • I tried Select-Object -Last 1 as the last line in the loop but unfortunately it did not work, so I went the route of running the command with the single output which worked out great. – codepoor May 22 '18 at 21:50
  • Operations to reduce data should go as early in the pipeline as possible. I would probably put `Select-Object -Last 1` after (or before) `ConvertFrom-CSV`. – lit May 22 '18 at 22:02
  • @lit let's say i have 6 lines of GPU stats that are output with a single run of the command and i wanted to print those later, would i have to create an array to store it for retrieval? – codepoor May 23 '18 at 18:06
  • You have it stored in the `$cmd` variable. That could be written to a file if you need to persist it. Investigate `Export-Csv` and `ConvertTo-Csv`. – lit May 23 '18 at 18:17
0

The biggest hurdle to overcome is the object orientation of PowerShell.

To start you on some familiar territory, you could use a regex to parse the numbers out of the string.

PS C:\src\t> $s = '2018/05/21 21:21:54.118, GeForce GTX 1060 6GB, 00000000:01:00.0, 63, 100 %, 93 %'
PS C:\src\t> $s -match '.*, (\d*) %, (\d*) %$'
True
PS C:\src\t> $Matches

Name                           Value
----                           -----
2                              93
1                              100
0                              2018/05/21 21:21:54.118, GeForce GTX 1060 6GB, 00000000:01:00.0, 63, 100 %, 93 %


PS C:\src\t> $Matches[1]
100
PS C:\src\t> $Matches[2]
93

Show us some of the code you might create from this and someone will probably have suggestions on going further.

lit
  • 14,456
  • 10
  • 65
  • 119
  • 3
    It's being exported as csv. He can literally just pipe to convertfrom-csv – Maximilian Burszley May 22 '18 at 02:38
  • 1
    OK so i just tried the following and it's blowing my mind: `$cmd = "& 'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi' --query-gpu=timestamp,name,pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory --format=csv -l 5"` `invoke-expression $cmd | ConvertFrom-CSV` It automatically associated labels like timestamp and name to the proper values - MAGIC. – codepoor May 22 '18 at 04:10