Get My Variable Revisited

Last year I wrote a few articles on working with variables. One task I needed was to identify the variables that I had created in a given PowerShell session with a function I wrote called Get-MyVariable. I also posted an article on identifying the object type for a variable value. While trying to find something today I realized I could combine the two ideas.

One approach would be to leave my original function alone. When I need the variable value type, I could simply to this:


PS C:\> get-myvariable | Select Name,Value,@{Name="Type";Expression={$_.Value.GetType().Name}}

Name Value Type
---- ----- ----
a 5/28/2012 8:45:25 AM DateTime
bigp ps | where {$_.ws -gt 1... ScriptBlock
cert [Subject]... X509Certificate2
dirt Param([string]$Path=$en... ScriptBlock
...

See the value in having a function that writes to the pipeline? However, I wanted this to be the default behavior so I decided to incorporate it into my original function. I also realized that there may be situations where I don’t want this information so I added a -NoTypeInformation switch parameter. The changes to my original function were minimal.


#filter out some automatic variables
$filtered=$variables | Where {$psvariables -notcontains $_.name -AND $_.name -notmatch $skip}

if ($NoTypeInformation) {
#write results with not object types
$filtered
}
else {
#add type information for each variable
Write-Verbose "Adding value type"
$filtered | Select-Object Name,Value,@{Name="Type";Expression={$_.Value.GetType().Name}}
}

Now, I can run a command like this:


PS S:\> get-myvariable | where {$_.type -eq "Scriptblock"} | Select name

Name
----
bigp
dc01
dirt
disk
doy
run
up

I suppose I could further refine the function to do filtering in place for a specific type. But I’ll leave that exercise to you.

Download Get-MyVariable2 and try it out for yourself.

Friday Fun What’s My Variable

I use scriptblocks quite a bit in my PowerShell work, often saved as variables. These are handy for commands you want to run again, but don’t necessarily need to turn into permanent functions.


$freec={(get-wmiobject win32_logicaldisk -filter "deviceid='c:'" -property Freespace).FreeSpace/1mb}

Now in PowerShell I can invoke the scriptblock.


PS S:\> &$freec
94079.72265625

Ok then. I have a number of these defined. I decided I wanted an easy way to identify them when I run Get-Variable. For example, if I remembered all the variable names I could just do this:


PS S:\> get-variable freec,dirt

Name Value
---- -----
freec (gwmi win32_logicaldisk -filter "deviceid='c:...
dirt Param([string]$Path=$env:temp) Get-ChildItem ...

But needless to say that’s asking too much. When I first looked at this problem I went down the path of trying to parse values I saw with Get-Variable to identify potential script blocks. Then I realized this was a rookie mistake. PowerShell is all about the objects. Now a variable is also an object with a value property. This value could be a string, and integer or a pscredential. So my task then was to identify each value type.

Every object in PowerShell has a built in method called GetType().


PS S:\> $s=get-service spooler
PS S:\> $s.GetType()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False ServiceController System.ComponentM...

This is actually another object with a Name property.


PS S:\> $s.GetType().name
ServiceController

Aha! Let’s look at this with my variable.


PS S:\> (get-variable freec).value.GetType().Name
ScriptBlock

This is a one-line shortcut that gets the Value property of the Freec variable and then runs the GetType() method followed by retrieving just the Name property. This is promising. Here’s one way I can use this:


get-variable | Where {$_.value.GetType().Name -eq "ScriptBlock"}

As you can see there is still an issue with variables with no values.

I’ll just add another condition to my Where expression.


get-variable | Where {$_.value -AND $_.value.GetType().Name -eq "ScriptBlock"}

Success!

These are in fact all of the scriptblocks in my current session. But now I can take this a step further and look at my other variables and their type.


get-variable | select Name,@{Name="Type";Expression={$_.value.GetType().Name}}

Or I might try grouping.


get-variable | select Name,@{Name="Type";Expression={$_.value.GetType().Name}} | where {$_.type} | Group Type | Sort Count -Descending

I wanted to filter out empty values so I’m only keeping objects that have a defined type in my grouped output.

The bottom line is never forget about the object!

Create a Read-Only PowerShell Session

In my PowerShell training class this week, I was demonstrating how to take advantage of the -Whatif and -Confirm parameters. These parameters exist (or should) for any cmdlet that changes the environment such as stopping a service, killing a process or copying a file.

The students liked that and clearly recognized the value. In fact, one student asked if there was a way to make that behavior the default. that is always use -whatif. Intriguing idea thought. I thought for a moment and realized there might be an automatic variable to controls that behavior. For example, to turn on the verbose pipeline I can change $VerbosePrefence to Continue.

I took a quick look at variables and tucked away, because I never had reason to notice it before, is $WhatIfPreference. If you look that value, it is False. So try this: set the value to True.

Now see what happens when you try to do something that would change the environment.

 

You can’t complete any of these expressions. Any command that supports Whatif will automatically use it. In essence I have turned my PowerShell session into a “read-only” experience. Of course this isn’t foolproof as this only applies to PowerShell cmdlets. I could still run the NET or SC command line tools. Setting the $WhatIfPreference back to false puts things back to normal. But for a novice user, you could setup a profile for them to modify the preference variable and trust that they wouldn’t get into any problems. I have to admit I’m intrigued by this and time permitting want to look into ways that this could be enforced for an end user but not for an admin, among other scenarios.

The long term solution for this sort of thing is a constrained runspace, but that requires a bit more PowerShell expertise and complexity. I love the simplicity of changing a single variable. The other take-away from this experience is to look at the PowerShell variables and make sure you understand their purpose and what changes you might make to improve your PowerShell experience.

Warning Signs

I was working on a project with an advanced PowerShell function. One of the goals was to take advantage of the common parameters like -ErrorVariable and -WarningVariable so that when you run the function you can save errors and warnings and work with them later. Turns out one of these works and one doesn’t. But I worked out a hack for the obstinate one. Continue reading “Warning Signs”

Get Variable Definition

Last week a question came across my email about how to find out where a variable came from. I thought this was a great question because I’ve run into this scenario as well. I see a variable but don’t recall what command I typed to create it. What I need is an easy way to find the command so I made a tool. Continue reading “Get Variable Definition”