
close
close
Chance to win $250 in Petri 2023 Audience Survey
I came across an interesting question the other day in a PowerShell forum. The poster wanted to include numbers for each line of output, which might look like this:
1. Stopped AdobeFlashPlaye... Adobe Flash Player Update Service 2. Stopped AeLookupSvc Application Experience 3. Stopped ALG Application Layer Gateway Service
I wasn’t sure at first why you would want to do this, as this feels like text parsing and not taking advantage of PowerShell’s object nature. But I started playing along. Here’s one approach.
get-service | foreach -Begin {$i=0} -Process {
$i++
"{0:D2}. {1} [{2}]" -f $i,$_.Name,$_.Status
}
Which gives you this:
A numbered output list created with Windows PowerShell. (Image Credit: Jeff Hicks)
get-service | where {$_.status -eq 'running'} | foreach -Begin {$i=0} -Process { $i++ "{0}. {1}" -f $i,$_.Name } -outvariable menu $r = Read-Host "Select a service to restart by number" Write-Host "Restarting $($menu[$r-1])" -ForegroundColor Green Restart-Service $menu[$r-1].Split()[1] -PassThru -force
I’m saving the results to $menu, so that I can access them later in the example.
An interactive console in Windows PowerShell. (Image Credit: Jeff Hicks)
$global:i=0
get-service | Select @{Name="Item#";Expression={$global:i++;$global:i}},Name,Displayname,Status
I am referencing the variable in the global scope because otherwise $i would be new for each object and everything would have a value of 1 in the Expression scriptblock. If I were to pipe this to Get-Member, I would see a new property called Item#. Given that, here’s my revised menu code:
$global:i=0 get-service | where {$_.status -eq 'running'} | Select @{Name="Item";Expression={$global:i++;$global:i}}, Name -OutVariable menu | format-table -AutoSize $r = Read-Host "Select a service to restart by number" $svc = $menu | where {$_.item -eq $r} Write-Host "Restarting $($svc.name)" -ForegroundColor Green Restart-Service $svc.name -PassThru –force
Our revised output. (Image Credit: Jeff Hicks)
Get-FormatData -TypeName System.ServiceProcess.ServiceController | Export-FormatData -Path c:\scripts\myservice.ps1xml -IncludeScriptBlock -force
You can also open up $pshome\DotNetTypes.format.ps1xml in the ISE or copy and paste the relevant section into a new file. Now, you can edit and create your own formatting file. Here’s what I came up with:
<?xml version="1.0" encoding="utf-8"?> <Configuration> <ViewDefinitions> <View> <Name>numbered</Name> <ViewSelectedBy> <TypeName>System.ServiceProcess.ServiceController</TypeName> </ViewSelectedBy> <TableControl> <TableHeaders> <TableColumnHeader> <Label>Item</Label> <Width>5</Width> </TableColumnHeader> <TableColumnHeader> <Width>8</Width> </TableColumnHeader> <TableColumnHeader> <Width>18</Width> </TableColumnHeader> <TableColumnHeader> <Width>38</Width> </TableColumnHeader> </TableHeaders> <TableRowEntries> <TableRowEntry> <TableColumnItems> <TableColumnItem> <Scriptblock>$global:i++; $global:i</Scriptblock> </TableColumnItem> <TableColumnItem> <PropertyName>Status</PropertyName> </TableColumnItem> <TableColumnItem> <PropertyName>Name</PropertyName> </TableColumnItem> <TableColumnItem> <PropertyName>DisplayName</PropertyName> </TableColumnItem> </TableColumnItems> </TableRowEntry> </TableRowEntries> </TableControl> </View> </ViewDefinitions> </Configuration>
I changed the Name setting because I will use this to reference this view. What I’m creating is in addition to the normal formatting. Next, I’ve created a new heading for the item number.
<TableColumnHeader> <Label>Item</Label> <Width>5</Width> </TableColumnHeader>
Naturally I need to provide a value.
<TableColumnItem> <Scriptblock>$global:i++; $global:i</Scriptblock> </TableColumnItem>
There is a concession here you have to accept. There is no easy way in the XML file to initialize $global:I every time you format the results.
The first time everything will look fine, but unless you reset $i it will keep increasing every time you format the output with this custom view. Once you have it complete, you can load it into your session and use it.
update-formatdata -AppendPath C:\scripts\myservice.ps1xml
get-service | format-table -View numbered
The get-service cmdlet in Windows PowerShell. (Image Credit: Jeff Hicks)
Update-TypeData -TypeName System.ServiceProcess.ServiceController -MemberType ScriptProperty -MemberName Item -Value {$global:i++; $global:i} -force
get-service | select Item,name,displayname,status
However, there is the same caveat about $global:i.
Although if you were using this in a script or function you could handle initializing the variable and cleaning up at the end.
Finally, you could take all of this one more step and create your own custom object with its own type and formatting directives, but I’ll leave that fun exercise to you.
More in PowerShell
Most popular on petri