Here is a simple function that splits a file path into its individual components and assigns them to a handful of properties:
function Get-FilePathComponents {
[CmdletBinding()]
param (
[Parameter(Mandatory,Position=0,ValueFromPipeline)]
[String[]]
$Path
)
begin {}
process {
foreach ($P in $Path) {
[PSCustomObject]@{
ContainingFolder = Split-Path $P -Parent
FileBaseName = Split-Path $P -LeafBase
FileFullName = Split-Path $P -Leaf
FileExtension = Split-Path $P -Extension
FullPathNoExtension = [IO.Path]::Combine((Split-Path $P -Parent),(Split-Path $P -LeafBase))
CompletePath = $P
ParentFolder = Split-Path (Split-Path $P -Parent) -Parent
}
}
}
}
I then use the function like this:
$DestFile = "C:\Images\Wallpapers\SomeCoolWallpaper.jpg"
$Components = Get-FilePathComponents $DestFile
$Components.FileFullName # Outputs SomeCoolWallpaper.jpg
$Components.ContainingFolder # Outputs C:\Images\Wallpapers
What I really need to accomplish is to somehow enable autocompletion for the returned object and its important properties.
This image below is exactly what I want:
I was able to get it to work by defining a custom types.ps1xml file and populating it with <ScriptProperty>
members that correspond to my parameters:
<Types>
<Type>
<Name>VSYSFileOps.Object.FilePathComponents</Name>
<ScriptProperty>
<Name>ContainingFolder</Name>
<GetScriptBlock>
$this.ContainingFolder
</GetScriptBlock>
</ScriptProperty>
<ScriptProperty>
<Name>FileBaseName</Name>
<GetScriptBlock>
$this.FileBaseName
</GetScriptBlock>
</ScriptProperty>
<ScriptProperty>
<Name>FileFullName</Name>
<GetScriptBlock>
$this.FileFullName
</GetScriptBlock>
</ScriptProperty>
... Etc...
And then adding [OutputType('VSYSFileOps.Object.FilePathComponents')]
to the beginning of my function. Surprisingly, while this seems to work, I've been informed by some very knowledgeable people that this is a very bad idea and not what ps1xml files are used for.
I really want to get auto-completion working for this. How can I make this happen?
Any help at all would be extremely welcomed.
Edit: I am using VSCode with the official PowerShell extension and Powershell Pro Tools.
Edit 2 (Working Solution):
Many thanks to @AdminOfThings. His advice led me to a great solution.
Here's the final working code:
$FilePathComponents = @'
public class FilePathComponents
{
public string ContainingFolder { get;set; }
public string FileBaseName { get;set; }
public string FileFullName { get;set; }
public string FileExtension { get;set; }
public string FullPathNoExtension { get;set; }
public string CompletePath { get;set; }
public string ParentFolder { get;set; }
}
'@
Add-Type -TypeDefinition $FilePathComponents -Language CSharp
function Get-FilePathComponents {
[OutputType("FilePathComponents")]
[CmdletBinding()]
param (
[Parameter(Mandatory,Position=0,ValueFromPipeline)]
[String[]]
$Path
)
begin {}
process {
foreach ($P in $Path) {
$obj = [FilePathComponents]::new()
$obj.ContainingFolder = Split-Path $P -Parent
$obj.FileBaseName = Split-Path $P -LeafBase
$obj.FileFullName = Split-Path $P -Leaf
$obj.FileExtension = Split-Path $P -Extension
$obj.FullPathNoExtension = [IO.Path]::Combine((Split-Path $P -Parent),(Split-Path $P -LeafBase))
$obj.CompletePath = $P
$obj.ParentFolder = Split-Path (Split-Path $P -Parent) -Parent
$obj
}
}
}
The key was adding [OutputType("FilePathComponents")]
to the beginning of the function definition. Now, when I use the function outside of the module, Intellisense and TAB completion work like a charm, even without strictly typing the variable name:
CodePudding user response:
If you want to go the custom class route, you can add a custom class as a type definition or by loading the class code in a script file/console:
# class definition
$class = @'
public class FilePathComponents
{
public string ContainingFolder { get;set; }
public string FileBaseName { get;set; }
public string FileFullName { get;set; }
public string FileExtension { get;set; }
public string FullPathNoExtension { get;set; }
public string CompletePath { get;set; }
public string ParentFolder { get;set; }
}
'@
# add class to PowerShell session
Add-Type -TypeDefinition $class
# define function
function Get-FilePathComponents {
[CmdletBinding()]
param (
[Parameter(Mandatory,Position=0,ValueFromPipeline)]
[String[]]
$Path
)
begin {}
process {
foreach ($P in $Path) {
$obj = [FilePathComponents]::new()
$obj.ContainingFolder = Split-Path $P -Parent
$obj.FileBaseName = Split-Path $P -LeafBase
$obj.FileFullName = Split-Path $P -Leaf
$obj.FileExtension = Split-Path $P -Extension
$obj.FullPathNoExtension = [IO.Path]::Combine((Split-Path $P -Parent),(Split-Path $P -LeafBase))
$obj.CompletePath = $P
$obj.ParentFolder = Split-Path (Split-Path $P -Parent) -Parent
$obj
}
}
}
# call your function
$file = Get-FilePathComponents C:\temp\test1\a.csv
# use $file. to auto-populate the properties