Edit: Make sure to read the comments from Shay Levy and Richard on clarifying my mistaken assumptions about how things work in PowerShell. Thanks to Richard & Shay for the clarifications! 

As I’m a command line kind of guy, I look for every opportunity to stay in a PowerShell window at all times. “Why grab the mouse when you don’t have?” to is my motto! The other day I was working on my laptop and was running short of battery power so wanted to switch to the Power Saver plan. A quick search told me POWERCFG.EXE was the command line tool to use.

Hold on there Flash! POWERCFG.EXE with the –S option is the command line way to change power plans, but to specify the plan you must use the GUID of the plan. While I’m a big fan of GUIDs there’s several places where GUIDs are not appropriate: command line tools, URLs, and any other place where end users could see them. Shouldn’t you just be able to set the power plan by name like “Balanced, ” “Power Saver,” or if you are feeling randy, “High Performance?” That’s an oversight that needs to be fixed so below is a PowerShell script, Set-PowerPlan.ps1, that lets you set the power plan at the command line based on the name of the plan instead of an ugly GUID.

The basic idea of the script is to get the list of defined power plans from “POWERCFG.EXE –l” and parse the output with a regular expression looking for the plan name with matching GUID. With the GUID in hand calling “POWERCFG –s <GUID>” flips to the defined plan. The regular expression work was simple, but there was one thing that stumped me.

In all the PowerShell work I’ve done I’ve never needed to capture the output of an old-school command line program like POWERCFG.EXE. It seemed so simple to do something like “$plans = POWERCFG –l” to get the list of plans and parse the output with “$plans –match <some regex>” to pull out the GUID. Little did I know that would definitely not be anything approaching normal. Yet again a PowerShell-ism reached up and dope slap me hard.

Interactively in a PowerShell console window, I ran “$plans = POWERCFG –l” and double-checked that $plans was a string by executing “$plans | Get-Member” as piping to Get-Member will report the type of object. Sure enough I was looking at a System.String. Oddly, when I ran “$plans –match <regex>” it always returned false even though typing “$plans” definitely showed me the complete output of the “POWERCFG –l” and the exact string I was looking for was there.

What totally confused me was running “$plans.Length” as that reported “7” instead of the 364 characters I counted in the string. After a bunch of reading I finally realized that $plans is really a freaking ARRAY of seven lines instead of a complete string. When PowerShell captures the output of a regular command line program as an array of strings. Whenever one is first exposed to PowerShell you invariably are taught about the Get-Member command right at the beginning to figure out the type of an object. When Get-Member lies to you it’s hard to get a clue. It’s things like this that make you understand why so few developers have switched over to PowerShell.

If you want all the output of a regular command line program into an actual string you have two choices. The first is to explicitly type the receiving variable with [string] like the following: “[string]$plans = powercfg -l” The alternative is to gather the regular command output into a variable, but if you need to look across all that output pipe the variable array to Out-String surrounded by parenthesis as in “($plans | Out-String)”.

I’m sure some PowerShell Ninja will jump in here and tell me how I missed the obvious, but I don’t feel I did. Hopefully, this little PowerShell gotcha will help you go down the PowerShell route with more confidence. While PowerShell has some bumps along the way the journey is totally worth it!

Set-PowerPlan.PS1
  1. #requires -version 2.0
  2.  
  3. # (c) 2012 by John RobbinsWintellect
  4.  
  5. <#.SYNOPSIS
  6. Sets the current power plan.
  7. .DESCRIPTION
  8. The POWERCFG.EXE utility requires GUIDS when changing a power plan instead of
  9. the name of the plan. That’s highly inconvenient so this simple script allows
  10. you to use common sense names like “Balanced” or “Power Saver” instead.
  11.  
  12. To get the list of power plans on your computer execute ‘powercfg -l’
  13. .PARAMETER Plan
  14. The name of the power plan to use.
  15. .EXAMPLE
  16. Set-PowerPlan -Plan Balanced
  17. Sets the power plan to the Balanced plan, the recommnded Microsoft plan.
  18. .EXAMPLE
  19. Set-PowerPlan “Power Saver”
  20. Sets the power plan to the Power Saver plan to reduce battery usage.
  21. .LINK
  22. http://www.wintellect.com/CS/blogs/jrobbins/archive/tags/PowerShell/default.aspx
  23. #>
  24.  
  25. param ($Plan = $(throw ‘You must specify the power plan, use “powercfg -l” for the plan names’ ))
  26.  
  27. Set-StrictMode -Version Latest
  28.  
  29. # Get the list of plans on the current machine.
  30. $planList = powercfg.exe -l
  31.  
  32. # The regular expression to pull out the GUID for the specified plan.
  33. $planRegEx = “(?<PlanGUID>[A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12})” + (“(?:s+({0}))” -f $Plan)
  34.  
  35. # If the plan appears in the list…
  36. if ( ($planList | Out-String) -match $planRegEx )
  37. {
  38.     # Pull out the matching GUID and capture both stdout and stderr.
  39.     $result = powercfg -s $matches[“PlanGUID”] 2>&1
  40.     
  41.     # If there were any problems, show the error.
  42.     if ( $LASTEXITCODE -ne 0)
  43.     {
  44.         $result
  45.     }
  46. }
  47. else
  48. {
  49.     Write-Error (“The requested power scheme ‘{0}’ does not exist on this machine” -f $Plan)
  50. }