PowerShell Potluck - May 2025
Here we are again at the end of another month. I hope you found a few useful bits of information this month. If you are a free subscriber, I hope you'll think about upgrading to a premium subscription, if only for a few months so you can see all that you've been missing. Use the links in the footer of this email to upgrade or manage your subscription.
Let's see what PowerShell odds and ends I have for you this month.
Microsoft Edit
Microsoft has made their legacy command line editor open source. Eventually, it will ship as part of Windows 11. In the meantime you can install it from the project's GitHub repository. You download assets for Windows and Linux and manually install, or you can install using winget.
winget install Microsoft.Edit -s winget
The executable, edit.exe
, should end up in your path, most likely in C:\Windows. You can easily open a file from the command prompt.
edit c:\scripts\company.txt

You can use the File menu to open other files.

This is a basic text editor. It is not PowerShell aware so don't expect syntax highlighting or other PowerShell features.

But it is a handy tool when you only need to make a minor edits to a file and you won't want to open VS Code or a full-blown IDE. Yes, you could use Notepad, but that opens another app. Edit
runs in the console window. It will not work over a traditional PowerShell remoting session, but it will work over SSH.
An alternative, which I've mentioned before, is the PSEdit
module. The Show-PSEditor
command, which has an alias of psedit
, is a pretty complete text editor that runs in the PowerShell console and will provide syntax highlighting and intellisense.

You can install the PSEdit
module from the PowerShell Gallery.
Microsoft Edit might make a more compelling case when it is integrated into Windows 11. I don't know what the plans are for server editions or support, although I copied the executable to a Windows Server 2019 machine and it worked fine.
WinUIShell
This next item is from the brilliant mind of mdgrs-mei who created the PowerShellRun and PoshTaskBarItem PowerShell modules I've mentioned in the past. The latest module is a PowerShell wrapper around WinUI. This is a Microsoft development framework for building Windows applications. The module is called WinUIShell. I think the goal is to create a framework module that you can use to build your own PowerShell applications using WinUI but without having to learn C# or the WinUI framework. You will be able to create modern interfaces to your PowerShell scripts and modules.

This is merely a mock-up of what the interface might look like. The module doesn't have commands in the traditional sense. Instead, loading the module loads the necessary assemblies that you can build from. There are several example scripts in the repository.
The module is very much in the beginning stages of development. I put together a quick proof-of-concept script that creates a clock.
#requires -version 7.5
#requires -module WinUIShell
#define a script block to run in a thread job
$sb = {
Import-Module WinUIShell
$defaultText = Get-Date
$win = [WinUIShell.Window]::new()
$win.Title = 'PowerShell Time'
$win.AppWindow.ResizeClient(500, 100)
$win.addClosed({ $script:running = $false })
$clockText = [WinUIShell.TextBlock]::new()
$clockText.Text = $defaultText
$clockText.FontSize = 48
$clockText.FontFamily = 'Bell MT' #'Segoe UI'
$clockText.HorizontalAlignment = 'Center'
$clockText.VerticalAlignment = 'Center'
$brush = [winUIShell.SolidColorBrush]::new()
$brush.Color = [WinUiShell.Colors]::HotPink
$clockText.Foreground = $brush
$win.Content = $clockText
$script:running = $True
$win.Activate()
do {
$clockText.Text = Get-Date
Start-Sleep -Seconds 1
} While ($script:running)
$win.WaitForClosed()
}
#run in a thread job
[void](Start-ThreadJob -ScriptBlock $sb -Name 'Clock' -StreamingHost $host)

I look forward to seeing how this develops. This could be a great alternative to using WPF.
Edit-ScriptFile
I am continuing to explore the scripting possibilities using the pwshSpectreConsole module. I often need to edit recent files, so I built a function that uses Read-SpectreMultiSelection to display a multiple selection prompt.
Function Edit-ScriptFile {
[CmdletBinding()]
[alias("esf")]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline
)]
[ValidateNotNullOrEmpty()]
[System.IO.FileInfo]$Path,
[ValidateNotNullOrEmpty()]
#these must be commands you can invoke from a command prompt
[ValidateSet("Code", "Notepad", "VSCodium","PowerShell_ISE")]
[string]$Editor = "Code"
)
Begin {
$files = @()
#use this color for the message and selection prompt
$formatColor = "SeaGreen3"
} #begin
Process {
#add each file to the array
$files+=$Path
} #process
End {
#sort the files by last write time so the most recent is first
$files = $files | Sort-Object LastWriteTime -Descending
#use inline formatting of the message prompt supported by pwshSpectreConsole
$splat = @{
Message = "`n[$formatColor]Select one or more files to edit in $($Editor): [/]"
AllowEmpty = $true
Color = $formatColor
Choices = $Files
ChoiceLabelProperty = "Name"
PageSize = 10
}
#invoke the multi-selection
$r = Read-SpectreMultiSelection @splat
if ($r) {
$r.Foreach({
Write-SpectreHost "Opening [green italic]$($_.Fullname) in VS Code[/]"
#Start-Process $Editor -ArgumentList $_.FullName
& $Editor (Convert-Path $_.FullName)
})
}
else {
Write-SpectreHost "[OrangeRed1]No files selected[/]"
}
} #end
}
The function lets me specify an editing option, which defaults to VS Code. All I need to do is pipe a list of files to the function.

This is something I tend to do a lot, so I wrote a wrapper function to make it even easier.
Function Edit-Last {
[cmdletbinding()]
[alias("el")]
Param(
[Parameter(Position = 1)]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path $_})]
[string]$Path = "C:\Scripts",
[Parameter(Position = 0)]
[ValidateNotNullOrEmpty()]
[string]$Filter = "*.ps1",
[ValidateNotNullOrEmpty()]
[Int]$Last = 10
)
Get-ChildItem $Path -file -filter $Filter |
Sort-Object LastWriteTime |
Select-Object -last $last |
Edit-ScriptFile
}
Show-PSImage
Another function I wrote recently is a wrapper for Get-SpectreImage. The command has a bug that prevents it from processing an image when using a PowerShell PSDrive. The solution is to convert the path to a file system path.

This is tedious so I built a wrapper function that does the conversion for me.
Function Show-PSImage {
[CmdletBinding()]
[alias("PSImage")]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName
)]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path $_},ErrorMessage="Can't find {0}.")]
[ValidatePattern("\.(jpg|jpeg|png|gif|bmp)$",ErrorMessage="File must be a .jpg, .jpeg, .png, .gif or .bmp.")]
[ArgumentCompleter({Get-ChildItem *.png,*.gif,*.bmp,*.jpg})]
[string]$Path,
[ValidateNotNullOrEmpty()]
[int]$MaxWidth = 25
)
Process {
Get-SpectreImage -ImagePath (Convert-Path $path) -MaxWidth $MaxWidth
}
}
The function makes this much easier.
PS S:\> Show-PSImage gazoo.bmp
And, I also get tab completion and I can easily pipe files to the function.

I've been told that the path issue will be resolved in the next release. In the meantime, I now have an easy to use command to view images in the PowerShell console.
Scripting Challenge
This month's scripting challenge I think is a little easier. It is designed to get you familiar with syntax and language that you can apply to your work. The first task is to use PowerShell to calculate the first 50 prime numbers. Once you have that list, create another list of the differences between each prime number and the next. For example, the first two prime numbers are 2 and 3, so the difference is 1. The next two prime numbers are 3 and 5, so the difference is 2, and so on. Finally, display the sum of each two prime number pairs. If you need one more challenge, get the sum of the first and last prime numbers in your list, then the second and second to last, etc.
I'll share some solutions next month.
Summary
That wraps things up for this month. I'm frantically getting ready for my sessions at WorkPlace Ninja's UK in Edinburgh and the PowerShell Conference EU in Malmö, Sweden, which means I need to get content for you all lined and ready to go.
My attempt at the PowerShell challenge:
$prime_numbers = @() $prime_numbers += 2 #cheating? for ($i = 3; $prime_numbers.Count -lt 100; $i=$i+2) { #no even number is a prime except 2 $is_prime = $true $sqrt_i = [Math]::Sqrt($i) for ($j = 2; $j -le $i - 1; $j++) { if ($j -gt $sqrt_i) { #by definition if we reach the sqrt of the number, we can stop checking Write-Verbose "$j is greater than the square root of $i, we found a prime!"; break } If ($i % $j -eq 0) { Write-Verbose "$i isn't prime, it's divisible by $j or $j is greater than the square root of $i"; $is_prime = $false; break } } If ($is_prime -eq $true) { $prime_numbers += $i; Write-Host "Found a prime - $i! We have $($prime_numbers.Count) prime numbers..."; } } Write-Host "Found $($prime_numbers.Count) prime numbers - $($prime_numbers -join ', ')" Return $prime_numbers