SpectreConsole Toolmaking
One of the things I enjoy the most about PowerShell is the ability to easily create tools. Often, I'll realize I want to do something that isn't currently possible or wish there was an easier way to accomplish a task. Often, I'll find myself verbalizing the task I want to accomplish. In so doing, I often recognize what PowerShell command or concept I can use to achieve my goal. This almost always means creating a new PowerShell tool in the form of a function. If I end up with several related functions, I might even create a module.
I'm also the type of person who enjoys the process and is open to a journey. It is not unusual for me to start with one idea and in the process create additional tools. And even I still learn things in the process so it is a win-win. Actually, a triple win because I also get to share what I've learned with you.
The Challenge
My initial challenge centered on selecting files from a folder. I often want to select a group of files and do something with them. The challenge is when the files have different names or extensions which preclude using wild cards. Using a regular expression pattern in Where-Object
is possible but tedious. I want to make this as easy to use from a PowerShell prompt as possible.
I could use Out-GridView
or Out-ConsoleGridView
as an object picker.
dir c:\work\*.ps1 | Out-ConsoleGridView -Title "Select one or more files"
> Install the Microsoft.PowerShell.ConsoleGuiTools
module in PowerShell 7 to get this command.

Selected objects are passed to the pipeline.
Directory: C:\work
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 3/23/2023 6:23 PM 292 d.ps1
-a--- 3/23/2023 6:24 PM 27673 d2.ps1
-a--- 1/30/2025 9:08 AM 1570 dev-caesar2.ps1
-a--- 1/26/2025 5:16 PM 4029 dev-topproc.ps1
It wouldn't be too difficult to create a function that wraps this command. However, this felt a little clunky. I wanted something more elegant.
I know that the pwshSpectreConsole
module has a several list commands. I've written about this module in the past. I was intrigued with the idea of a richer, more colorful experience. I was also looking for an excuse to do more with the module.
Read-SpectreMultiSelection
I knew I wanted to be able to select multiple files which led me to Read-SpectreMultiSelection. After reading the help, I did a quick proof-of-concept test from the PowerShell prompt.
$files = dir c:\work\*.ps1
$paramHash = @{
allowEmpty = $True
Message = 'Select one or more files'
Choices = $files
ChoiceLabelProperty = 'Name'
PageSize = 10
Color = "Gold1"
}
Read-SpectreMultiSelection @paramHash

> As of version 2.3 of the pwshSpectreConsole
module, you can't pipe objects to Read-SpectreMultiSelection
. You must use the Choices
parameter. I've been told that you will be able to pipe objects to this command in the next release.
Out-SelectFile
Now that I know what I want to do, I can take this code and wrap it into a function. It makes sense to expose Read-SpectreMultiSelection
parameters as function parameters, although I will likely set some defaults.
The major decision was to determine how my new function would fit into the PowerShell ecosystem. How would I want to use it? I could design the function to get the files and then present them in the list. That would mean a Path
parameter. I'd also need something to filter the files. Or, I could create a function that only wraps the Read-SpectreMultiSelection
command. I can let function only process the files passed to it and present them for selection. Getting the files would be up to me. Selecting them from a list with my new command would be a separate step.
This is the direction I chose. This is also more in line with the PowerShell philosophy of functions doing one thing. Getting files is one step. I can then pipe the files to my selection function, which will write selected files to the pipeline. I can then pipe the selected files to another command such as Copy-Item
or Remove-Item
.
#requires -version 7.5
#requires -module pwshSpectreConsole
Function Out-SelectFile {
<#
.SYNOPSIS
Select files from a SpectreConsole list
.DESCRIPTION
Select files using Read-SpectreMultiSelection from the pwshSpectreConsole module. Selected objects will be written to the pipeline.
The filenames must be unique.
.EXAMPLE
PS C:> dir c:\temp -file | Out-SelectFile | Remove-Item
.LINK
Read-SpectreMultiSelection
#>
[CmdletBinding()]
[alias('osf')]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[ValidateNotNullOrEmpty()]
[System.IO.FileInfo[]]$InputObject,
[Parameter(HelpMessage = "Specify the select page size greater or equal to 5.")]
[ValidateScript({$_ -gt 5},ErrorMessage = "Specify a page size >= 5")]
[int32]$PageSize = 25,
[Parameter(HelpMessage = "Specify a pwshSpectreConsole color")]
[alias("Color")]
[string]$SelectionColor = "Violet",
[Parameter(HelpMessage = "Specify a timeout value in seconds.")]
[ValidateScript({$_ -gt 0})]
[int]$Timeout
)
Begin {
#initialize an empty list to hold the files
$list = [System.Collections.Generic.List[System.IO.FileInfo]]::new()
} #begin
Process {
` #add each file to the list
foreach ($file in $InputObject) {
$list.Add($file)
}
} #process
End {
if ($list.Count -gt 0) {
Write-Information $list
#get the top-most directory path
$top = $list.DirectoryName | Sort-Object Length | Select-Object -first 1
$splat = @{
Message = "`nSelect file(s) to process from [SpringGreen1]$($top)[/]:"
Choices = $list
ChoiceLabelProperty = 'Name'
Color = $SelectionColor
AllowEmpty = $True
PageSize = $PageSize
}
If ($Timeout -gt 0) {
$splat.Add('TimeoutSeconds',$TimeOut)
}
Read-SpectreMultiSelection @splat
}
} #end
}