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!
Cloud management is difficult to do manually, especially if you work with multiple cloud…
Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…
https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…
FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…
Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…
In the intricate landscape of modern business, compliance is both a cornerstone of operational integrity…
View Comments
Worked out what your problem was as soon as I read the start of paragraph 4.
Been there, got the t-shirt, no, a whole wardrobe of t-shirts.
PowerShell is keen to enumerate collections when passing as parameters, and /extremely/ keen when going through the pipe (and you have a one element pipeline, with an implicit Out-Default at the end).
I would have probably stuck with the separate lines, with a regex that captured both fields: Guid & Name:
$info = PowerCfg -l | ? { $_ -match TheRegex } | % { select @{l='Guid';e={$matches["Guid"]}}, @{l='Name';e={$matches["Name"]}}
which leaves $info containing an array of objects { Guid, Name }, albeit in a script I would not use all those aliases (including in the hashes passed to Select-Object: label, expression).
Hi John
PowerShell unravel arrays and sends its items, the strings, one by one, one at a time, through the pipeline, that's how the pipeline works in PowerShell. That's why you see the type as String when you pipe to Get-Member. You could get the type of $plans by calling the GetType method (e.g $plans.GetType() ) or by using Get-Member's InputObject parameter (e.g gm -inp $plans).
With regard to changing power plans, consider the following, no string parsing needed:
Get-WmiObject -Class Win32_PowerPlan -Namespace rootcimv2power -Filter "ElementName='Balanced'" | Invoke-WmiMethod -Name Activate
Hi Richard & Shay,
Doh! I always forget about the pipeline. :( Sometimes the obvious escapes me. I'm learning my lesson: use .GetType() to double check the type.
Thanks for the corrections! I really appreciate you both taking the time to write.
- John Robbins
Hi John,
If you use
Get-Member -InputObject $plans
It will show you the correct type.
It's when you pipe something to Get-member that it will tell you _all_ the types in what ever is in the pipe.
To see the difference, try
1, 'a' | get-member
vs
get-member -InputObject (1,'a')
It bit me more than once before I got it...
Here is another way to do it from MSDN
http://blogs.msdn.com/b/aaronsaikovski/archive/2011/04/21/setting-your-machine-power-plan-via-powershell.aspx