Tag Archives: Out-Gridview

Friday Fun A Graphical PowerShell History Picker

One of my favorite features in PowerShell 3.0 is that you can select items in Out-Gridview which will then pipe the object back to the pipeline. One way I’ve been using this is as graphical “picker” for command history. I use Get-History, actually its alias h, all the time. Once I know the history number I then use Invoke-History, or its alias r. Now, with Out-Gridview I can sneak in a little something extra.


PS C:\> h | ogv -p | r

I realize this is cryptic but when using the shell interactively efficiency is paramount. This is the actual full command.


PS C:\> Get-History | Out-Gridview -passthru | Invoke-History

I’ll get something like this:

history-picker

I can select an item, click OK and the command will run back in my console. There are a few downsides, but remember this is a Friday Fun article. First, if you cancel, PowerShell will invoke the last command again. Also, as written if you select multiple items, which you could with -Passthru, you’ll get an error because Invoke-History won’t accept multiple entries. You could force Out-Gridview to only allow a single selection.


PS C:\> h | ogv -OutputMode Single | r

Or you could use Foreach to handle multiple selections.


PS C:\> h | ogv -p | %{r $_}

Be aware that when you run multiple commands in the same pipeline formatting can get a little screwy so I would probably stay way from it.

One more way you might use this is with Invoke-Expression instead of Invoke-History. The history object has a commandline property which you could invoke.


PS C:\> (h | select * | ogv -p).Commandline | iex

This will also handle multiple commands. If you cancel from Out-Gridview, Invoke-Expression will throw an exception but at least it won’t try to re-run the last command which is probably better.

Again, these are all “quick and dirty” commands to have fun with from a PowerShell prompt.

PowerShell Graphing with Out-Gridview

I’ve received a lot of interest for my last few posts on graphing with the PowerShell console. But I decided I could add one more feature. Technically it might have made more sense to turn this into a separate function, but I decided to simply modify the last version of Out-ConsoleGraph. The new version adds a switch parameter called -Gridview. When used, the graphing information is sent to the Out-Gridview cmdlet. This version will require PowerShell 3.0 as I’ll explain.

I realized I could just as easily send the values for the object name, property and graphing figure to Out-Gridview. In the ForEach construct where each object is processed, if the -GridView parameter is specified, I’ll create an ordered hash table and add it to an array.


...
if ($GridView) {
#add each object to the gridview data array
$gvHash = [ordered]@{
$CaptionProperty = $caption
$Property = ($g*$graph)
Value = $obj.$Property
}
$gvData += New-Object -TypeName PSObject -Property $gvHash
}
...

I need an ordered hash table so that the property values remain in the same order. I could have piped the new object to Select-Object and specified the order, but hopefully many of you are moving to v3 anyway. There’s no need for me to pad the caption since the grid view lines things up in columns automatically. It is also easier to add a property with the actual property value. I wanted to incorporate that value into the console version, but it proved to be more complicated than expected. Using Out-Gridview is a nice alternative.

After all of the objects have been processed, if there is anything in $gvData, I send it Out-Gridview.


...
if ($gvData) {
Write-Verbose "Sending data to Out-Gridview"
$gvData | Out-GridView -Title $Title
}
...

When using the grid, there is no way to specify color. But on the other hand, it is much easier to click on a column heading and re-sort. Here’s a command I ran with the new function.


Get-ChildItem C:\Scripts -Directory | foreach {
$data = Get-ChildItem $_.FullName -Recurse -File | Measure-Object -Property Length -sum
$_ | Select Name,@{Name="Size";Expression={$data.sum}}
} | Out-ConsoleGraph -Property Size -Title "Scripts Folder Report" -grid

out-consolegraph-gv

Now I have a tool with some flexibility. Although you might also be interested in this article from last November on building a drive report graph also using Out-Gridview.

I bumped the latest version of Out-Consolegraph to 3.0 to align it with the file name. Download Out-ConsoleGraph-v3

Friday Fun: Edit Recent File

As you might imagine I work on a lot of PowerShell projects at the same time. Sometimes I’ll start something at the beginning of the week and then need to come back to it at the end of the week. The problem is that I can’t always remembered what I called the file. So I end up listing files, sorting by last write time and then looking at the last 10 or so. Time to be more efficient so I came up with a little PowerShell v3 function called Edit-RecentFile.


#requires -version 3.0

Function Edit-RecentFile {

<#
.Synopsis
Open selected files for editing in the PowerShell ISE
.Description
This command will list the most recently modified files in the specified
directory. By default the command will list the 25 most recently modified
files. Using Out-Gridview as an interface, you can select the files you
wish to edit and they will open in the PowerShell ISE. If you are already in
the ISE then each file will open in a new tab.

This command requires PowerShell v3.
.Example
PS C:\> edit-recentfile d:\myscripts -last 10
.Example
PS C:\> edit-recentfile c:\work\*.txt
#>

[cmdletbinding()]
Param(
[Parameter(Position=0)]
[ValidateScript({Test-Path $_})]
[string]$Path="c:\scripts",
[Parameter(Position=1)]
[ValidateNotNullorEmpty()]
[int]$Last=25,
[switch]$Recurse
)

Write-Verbose "Getting $last recent files from $path."

#get last X number of files from the path and pipe to Out-Gridview
$files = Get-ChildItem -Path $Path -file -Recurse:$Recurse |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -First $Last -Property FullName,LastWriteTime,Length,Extension |
Out-GridView -Title "Select one or more files to edit and click OK" -PassThru

#if files were selected, open them in the ISE
if ($files) {
Write-Verbose "Opening`n$($files.fullname | out-string)"
#if in the ISE use PSEDIT
if ($host.name -match "ISE") {
Write-Verbose "Detected the PowerShell ISE"
psedit $files.Fullname
}
else {
#otherwise assume we're in the console
Write-Verbose "Defaulting to PowerShell console"
ise ($files.FullName -join ",")
}
} #if $files

} #close Edit-RecentFile

The core section gets the directory listing of the most recently modified files but then pipes them to Out-Gridview.

Edit-RecentFile

In PowerShell v3, Out-Gridview has been extended so that you can select one or more items and pass them back to the pipeline.


$files = Get-ChildItem -Path $Path -file -Recurse:$Recurse |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -First $Last -Property FullName,LastWriteTime,Length,Extension |
Out-GridView -Title "Select one or more files to edit and click OK" -PassThru

Once I have the collection of files, I can use the FullName property and open them in the PowerShell ISE. From the console I can use the ise alias and pass a comma separated list of files. The fun part is turning the array of file paths into a comma separated string. But that is easily done using the -Join operator.


ise ($files.FullName -join ",")

If I am in the PowerShell ISE already, I can do a similar thing using the PSEDIT command, although this takes the array without any reformatting.


if ($host.name -match "ISE") {
Write-Verbose "Detected the PowerShell ISE"
psedit $files.Fullname
}

The function has a default path set to C:\Scripts which you might want to change. By default the function will only look in the root of the folder, but you can use -Recurse. The download file will also create an alias erf, for the function.

Download Edit-RecentFile and let me know how it works out for you.

Friday Fun: A GridView Drive Report

I’ve been experimenting with different techniques to work with PowerShell in graphical ways, but without resorting to complex solutions such as WinForms or ShowUI. For today’s Friday Fun I have a little script that presents a drive usage report using WMI and Out-GridView. As always, my goal with these articles is to impart a nugget of useful information, regardless of whether you need the complete solution.

Getting the drive data with WMI is pretty straightforward.


get-wmiobject win32_logicaldisk -filter "drivetype=3"

But what I want is something I can send to Out-Gridview that will give me a graphical representation of drive utilization. So I’ll take this basic command and pipe it to Select-Object, and add a few custom properties.


$data = Get-WmiObject -class win32_logicaldisk -ComputerName $computername -filter 'drivetype=3' |
Select @{Name="Computername";Expression={$_.Systemname}},
@{Name="Drive";Expression={$_.DeviceID}},
@{Name="SizeMB";Expression={[int]($_.Size/1MB)}},
@{Name="FreeMB";Expression={[int]($_.Freespace/1MB)}},
@{Name="UsedMB";Expression={[math]::round(($_.size - $_.Freespace)/1MB,2)}},
@{Name="Free%";Expression={[math]::round(($_.Freespace/$_.Size)*100,2)}},
@{Name="FreeGraph";Expression={
[int]$per=($_.Freespace/$_.Size)*100
"|" * $per }
}

My custom properties reformat values into MB and a Free percentage. These values are formatted as numbers so they can be sorted. That much would be fine if you wanted to write that to the pipeline. But I’m going to get graphical so I also define a property called FreeGraph. The value is simply the “|” character displayed once for each percent of free space. Sure, I could write this to the pipeline and see something like this:


Computername : SERENITY
Drive : G:
SizeMB : 476938
FreeMB : 102887
UsedMB : 374050.49
Free% : 21.57
FreeGraph : ||||||||||||||||||||||

But where this gets really interesting is where I pipe $data to Out-GridView. Here’s an example where I queried several computers.

Now I have a graphical report that I can filter and sort. You may have to manually resize the display and adjust columns but you get the idea. But as they say on late night TV, wait there’s more.

If you have PowerShell v3, Out-Gridview now supports passthru. You can select one or more objects in Out-GridView and they will be written to the pipeline. This leads to some interesting opportunities. Here’s a variation that opens the selected drives in a new gridview window, displaying all the WMI properties.


$data | out-gridview -Title 'WMI Detail: Please select one or more drives' -PassThru |
foreach {
get-wmiobject -Class win32_logicaldisk -filter "deviceid='$($_.Drive)'" -computername $_.Computername |
Select * } | Out-GridView -Title 'WMI Drive Detail'

This gridview allows me to select multiple entries.

When I click OK, the objects are piped back to PowerShell and in this case back to Out-Gridview.

Or, maybe you simply need to open the drive on the remote machine. Since I’m querying logical disks, they should each have an administrative share which I can construct from the drive object, and then open using Invoke-Item.


$data |
out-gridview -Title "Drive Explorer: Please select one or more drives to open" -PassThru |
Foreach {
#construct a UNC for the drive that should be the administrative share
$unc = "\\{0}\{1}" -f $_.Computername,$_.Drive.replace(":","$")
#open the UNC in Windows Explorer
invoke-item $unc
}

In these examples Out-Gridview is set to allow multiple selections. If you prefer to limit selection to one object, then use -Outputmode Single in place of -Passthru.

I’ve put all of these commands in a script you can download and try for yourself.

PowerShell Hyper-V Memory Report

Since moving to Windows 8, I’ve continued exploring all the possibilities around Hyper-V on the client, especially using PowerShell. Because I’m trying to run as many virtual machines on my laptop as I can, memory considerations are paramount as I only have 8GB to work with. Actually less since I still have to run Windows 8!

Anyway, I need to be able to see how much memory my virtual machines are using. The Get-VM cmdlet can show me some of the data.


PS C:\> get-vm chi-dc03

Name State CPUUsage(%) MemoryAssigned(M) Uptime Status
---- ----- ----------- ----------------- ------ ------
CHI-DC03 Running 0 559 23:29:12 Operating normally

Actually, there are more properties I can get as well.


PS C:\> get-vm chi-dc03 | select *mem*

MemoryAssigned : 586153984
MemoryDemand : 491782144
MemoryStatus : OK
MemoryStartup : 402653184
DynamicMemoryEnabled : True
MemoryMinimum : 402653184
MemoryMaximum : 1073741824

Those values are in bytes so I would need to reformat them to get them into something more meaningful like bytes. Not especially difficult, but not something I want to have to type all the time. Now, I can also get memory information with Get-VMMemory and this is formatted a little nicer.


PS C:\> get-vmmemory chi-dc03

VMName DynamicMemoryEnabled Minimum(M) Startup(M) Maximum(M)
------ -------------------- ---------- ---------- ----------
CHI-DC03 True 384 384 1024

What I like about this cmdlet is that it also shows the buffer and priority settings.


PS C:\> get-vmmemory chi-dc03 | select Startup,Buffer,Priority,Minimum,Maximum

Startup : 402653184
Buffer : 20
Priority : 50
Minimum : 402653184
Maximum : 1073741824

In the end, I decided the best course of action was to build my own function that combined information from both cmdlets. The result is a custom object that gives me a good picture of memory configuration and current use. The function, Get-VMMemoryReport, is part of a larger HyperV toolkit module I’m developing but I thought I’d share this with you now.


Function Get-VMMemoryReport {
#comment based help is here

[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a VM",
ValueFromPipeline=$True)]
[alias("VM")]
[object]$Name,
[ValidateNotNullorEmpty()]
[string]$Computername=$env:COMPUTERNAME
)

Process {
if ($Name -is [String]) {
Try {
$Name = Get-VM -name $Name -ComputerName $computername -ErrorAction Stop
}
Catch {
Write-Warning "Failed to find VM $vm on $computername"
Return
}
} #if
elseif ($name -isnot [Microsoft.HyperV.PowerShell.VirtualMachine]) {
Write-Warning "You did not pass a string or a VM object"
Return
}

#get memory values
$memorysettings = Get-VMMemory -VMName $Name.name -ComputerName $Computername

#all values are in MB
$hash=[ordered]@{
Name = $Name.Name
Dynamic = $Name.DynamicMemoryEnabled
Assigned = $Name.MemoryAssigned/1MB
Demand = $Name.MemoryDemand/1MB
Startup = $Name.MemoryStartup/1MB
Minimum = $Name.MemoryMinimum/1MB
Maximum = $Name.MemoryMaximum/1MB
Buffer = $memorysettings.buffer
Priority = $memorysettings.priority
}

#write the new object to the pipeline
New-Object -TypeName PSObject -Property $hash

} #process
} #end Get-VMMemoryReport

I wrote the function with the assumption of piping Hyper-V virtual machines to it. Although I can also pipe names to it and the function will then get the virtual machine.


if ($Name -is [String]) {
Try {
$Name = Get-VM -name $Name -ErrorAction Stop
}
Catch {
Write-Warning "Failed to find VM $vm"
Return
}
} #if
elseif ($name -isnot [Microsoft.HyperV.PowerShell.VirtualMachine]) {
Write-Warning "You did not pass a string or a VM object"
Return
}

Once the function has the virtual machine object, it also gets data from Get-VMMemory.


$memorysettings = Get-VMMemory -VM $Name

Finally, it creates a hash table using the new [ordered] attribute so that the key names will be displayed in the order I enter them. I use this hash table to write a custom object to the pipeline. I could have used the new [pscustomobject] attribute as well, but I felt in a script using New-Object was a bit more meaningful. With this command, I get output like this:


PS C:\> get-VMMemoryreport chi-dc03

Name : CHI-DC03
Dynamic : True
Assigned : 559
Demand : 469
Startup : 384
Minimum : 384
Maximum : 1024
Buffer : 20
Priority : 50

Or I can explore the data in other ways. I can create an HTML report, export to a CSV or take advantage of Out-GridView.


PS C:\> get-vm | where state -eq running | get-vmmemoryreport | out-gridview -title 'VM Memory Report'

Here’s the report for my currently running virtual machines.

The function defaults to connecting to the localhost, but I am assuming that if you have an Hyper-V server you could use this from any system that has they Hyper-V module also installed. I don’t have a dedicated Hyper-V server to test with so maybe someone will confirm this for me.

In the meantime, download Get-VMMemoryReport and let me know what you think.

Friday Fun: Get Latest PowerShell Scripts

Probably like many of you I keep almost all of my scripts in a single location. I’m also usually working on multiple items at the same time. Some times I have difficult remembering the name of a script I might have been working on a few days ago that I need to return to. The concept is simple enough: search my script directory for PowerShell files sorted by the last write time and look for the file I need.

That’s a lot to type so why not build a function to do the work for me? In fact, since I spend a lot of time in the PowerShell ISE, why not produce graphical output? The easiest way is to pipe results to Out-Gridview. So after a little tinkering, I came up with Get-LatestScript.


Function Get-LatestScript {

[cmdletbinding()]

Param(
[Parameter(Position=0)]
[ValidateScript({Test-Path $_})]
[string]$Path=$global:ScriptPath,
[Parameter(Position=1)]
[ValidateScript({$_ -ge 1})]
[int]$Newest=10
)

if (-Not $path) {
$Path=(Get-Location).Path
}

#define a list of file extensions
$include="*.ps1","*.psd1","*.psm1","*.ps1xml"

Write-Verbose ("Getting {0} PowerShell files from {1}" -f $newest,$path)

#construct a title for Out-GridView
$Title=("Recent PowerShell Files in {0}" -f $path.ToUpper())

Get-ChildItem -Path $Path -Include $include -Recurse |
Sort-Object -Property lastWriteTime -Descending |
Select-Object -First $newest -Property LastWriteTime,CreationTime,
@{Name="Size";Expression={$_.length}},
@{Name="Lines";Expression={(Get-Content $_.Fullname | Measure-object -line).Lines}},
Directory,Name,FullName |
Out-Gridview -Title $Title

}

I decided to try something different with the Path variable. I set the default to a global variable, ScriptPath. The idea is that in your PowerShell profile, you’ll have a line like this:


$ScriptPath="C:\Scripts"

If the function finds this variable, it will use it. Otherwise it will use the current location. Notice I’m also using a validation attribute to verify the path. By default the function returns the 10 newest PowerShell files, based on the last write time. The number of files can be controlled by the -Newest parameter.

In the heart of the script is a one-line expression to find all matching files in the script folder and subfolders.


Get-ChildItem -Path $Path -Include $include -Recurse |
Sort-Object -Property lastWriteTime -Descending

These files are then sorted on the LastWriteTime in descending order and then I only select the first $newest number of files.


| Sort-Object -Property lastWriteTime -Descending |
Select-Object -First $newest ...

I am only interested in a few file properties so I select them. I also add a custom property to measure the file and get the number of lines in the script.


...-Property LastWriteTime,CreationTime,
@{Name="Size";Expression={$_.length}},
@{Name="Lines";Expression={(Get-Content $_.Fullname | Measure-object -line).Lines}},
Directory,Name,FullName

Finally the results are piped to Out-Gridview.


... | Out-Gridview -Title $Title

For now, I have to manually open the file. Perhaps I’ll create a WinForm or use ShowUI to integrate it into the PowerShell ISE.

You can download Get-LatestScript which includes comment based help.

Friday Fun: PowerShell ISE Function Finder

At the PowerShell Deep Dive in San Diego, I did a lightning session showing off something I had been working on. Sometimes I don’t know what possesses me, but I felt the need for a better way to navigate my PowerShell scripts files that had many functions. Some files, especially modules, can get quite long and contain a number of functions. When using the PowerShell ISE I wanted a faster way to jump to a function. The problem is I don’t always remember what I called a function or where it is in the file. It is very easy to jump to a particular line in the ISE using the Ctrl+G shortcut.

So I started with some basics.


$Path=$psise.CurrentFile.FullPath

I decided I’d use a regular expression pattern to find my functions. I write my functions like this:


Function Get-Foo {

Param()
...

So I came up with a regex pattern to match the first line and to include the Filter keyword as well.


[regex]$r="^(Function|Filter)\s\S+\s{"

I first thought of searching the content of the current file, but that won’t give me a line number. Then I thought of Select-String. With my regex pattern, I can get the content of the current file and pipe it to Select-String. The match object that comes out the other end of the pipeline includes a line number property (awesome) and the matching line. I decided to do a little string parsing on the later to drop off the trailing curly brace.


$list=get-content $path |
select-string $r | Select LineNumber,
@{Name="Function";Expression={$_.Line.Split()[1]}}

Because I’m in the ISE I felt the need to stay graphical, so my first thought was to pipe the results to Out-Gridview.


$list | out-gridview -Title $psise.CurrentFile.FullPath

Here’s a sample result.

Now I can see the function name and line number. In the ISE I can do Ctrl+G and jump to the function. Of course, if I modify the file and line numbers change I need to close the grid and re-run my command. But wait, there’s more….

I’ve never done much with the WPF and figured this would be a great opportunity to do something with the ShowUI module. I already had the data. All I had to do was create a form with ShowUI. This is what I ended up with.


[string]$n=$psise.CurrentFile.DisplayName
ScrollViewer -ControlName $n -CanContentScroll -tag $psise.CurrentFile.FullPath -content {
StackPanel -MinWidth 300 -MaxHeight 250 `
-orientation Vertical -Children {
#get longest number if more than one function is found
if ($count -eq 1) {
[string]$i=$list.Linenumber
}
else {
[string]$i=$list[-1].Linenumber
}
$l=$i.length
foreach ($item in $list) {

[string]$text="{0:d$l} {1}" -f $item.linenumber,$item.function

Button $text -HorizontalContentAlignment Left -On_Click {
#get the line number
[regex]$num="^\d+"
#parse out the line number
$goto = $num.match($this.content).value
#grab the file name from the tab value of the parent control
[string]$f= $parent | get-uivalue
#Open the file in the editor
psedit $f
#goto the selected line
$psise.CurrentFile.Editor.SetCaretPosition($goto,1)
#close the control
Get-ParentControl | Set-UIValue -PassThru |Close-Control
} #onclick
} #foreach
}
} -show

The result is a stack panel of buttons in a scroll control. The button shows the line number and function name.

When a button is clicked, the function gets the line number and automatically jumps to it. Originally I was leaving the control open, but this means the function is still running. And if I change the script the line numbers are off so I simply close the form after jumping to the function.

In the end, I packaged all of this as a script file that adds a menu choice. If ShowUI is available, the function will use it. Otherwise the function defaults to Out-GridView.


Function Get-ISEFunction {

[cmdletbinding()]
Param([string]$Path=$psise.CurrentFile.FullPath)

#import ShowUI if found and use it later in the functoin
if (Get-module -name ShowUI -listavailable) {
Import-Module ShowUI
$showui=$True
}
else {
Write-Verbose "Using Out-GridView"
$showui=$False
}

#define a regex to find "function | filter NAME {"
[regex]$r="^(Function|Filter)\s\S+\s{"

$list=get-content $path |
select-string $r | Select LineNumber,
@{Name="Function";Expression={$_.Line.Split()[1]}}

#were any functions found?
if ($list) {
$count=$list | measure-object | Select-object -ExpandProperty Count
Write-Verbose "Found $count functions"
if ($showui) {
<#
display function list with a WPF Form from ShowUI
Include file name so the right tab can get selected
#>

[string]$n=$psise.CurrentFile.DisplayName
Write-Verbose "Building list for $n"

ScrollViewer -ControlName $n -CanContentScroll -tag $psise.CurrentFile.FullPath -content {
StackPanel -MinWidth 300 -MaxHeight 250 `
-orientation Vertical -Children {
#get longest number if more than one function is found
if ($count -eq 1) {
[string]$i=$list.Linenumber
}
else {
[string]$i=$list[-1].Linenumber
}
$l=$i.length
foreach ($item in $list) {

[string]$text="{0:d$l} {1}" -f $item.linenumber,$item.function
Write-Verbose $text
Button $text -HorizontalContentAlignment Left -On_Click {
#get the line number
[regex]$num="^\d+"
#parse out the line number
$goto = $num.match($this.content).value
#grab the file name from the tab value of the parent control
[string]$f= $parent | get-uivalue
#Open the file in the editor
psedit $f
#goto the selected line
$psise.CurrentFile.Editor.SetCaretPosition($goto,1)
#close the control
Get-ParentControl | Set-UIValue -PassThru |Close-Control
} #onclick
} #foreach
}
} -show

} #if $showui
else {
#no ShowUI module so use Out-GridView
$list | out-gridview -Title $psise.CurrentFile.FullPath
}
}
else {
Write-Host "No functions found in $($psise.CurrentFile.FullPath)" -ForegroundColor Magenta
}

} #close function

#Add to the Add-ons menu
$PSISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("List Functions",{Get-ISEFunction},$null)

#optional alias
set-alias gif get-isefunction

Now, I can click the List Functions menu choice and I’ll get a graphical list of any functions in the current file. I’m sure the regex could be tweaked. I’m also sure there are improvements I could make to the ShowUI code, but it works.

Download Get-ISEFunction and let me know what you think.