I have a module that was written in PS5, and I'm adding a new function that requires PS7. I want to be able to run the function from PS5, so I'm thinking I can just have the function call pwsh
, but I need to be able to pass the function parameters into the pwsh
script block. Is this possible?
function Test-PS7 {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$String
)
pwsh {
Write-Host $String
}
}
When you call pwsh
, the PowerShell (Core) CLI, with a script block ({ ... }
) - which is only supported when calling from a PowerShell session - you can only pass positional arguments to that script block, using the -args
parameter (the same limitation applies to powershell.exe
, the Windows PowerShell CLI).
However, you can indirectly support passing named arguments by passing the automatic $PSBoundParameters
variable as a single, positional argument, and use splatting inside the script block to pass its value through to the target command.
The following demonstrates this via parameters that can be passed through to the Get-ChildItem
cmdlet, -LiteralPath
and -File
, as an example:
function Test-PS7 {
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $LiteralPath,
[switch] $File
)
pwsh {
param($boundParameters)
# Compensate for the problem of [switch] parameters not
# deserializing as such and splatting of [switch] parameters
# requiring *[bool]* values.
foreach ($key in @($boundParameters.Keys)) {
if ($boundParameters[$key].pstypenames -contains 'Deserialized.System.Management.Automation.SwitchParameter') {
$boundParameters[$key] = $entry.IsPresent
}
}
# Now @boundParameters can be used for splatting
Get-ChildItem @boundParameters
} -args $PSBoundParameters
}
Note:
To avoid potentially unnecessary overhead and to make for a more predictable execution environment, consider adding -NoProfile
to the pwsh
call.
Due to the limited type fidelity that cross-process serialization invariably entails (see this answer), [switch]
parameter turn into emulations which splatting doesn't recognize, necessitating the workaround above:
Specifically, these emulations are [pscustomobject]
(aka [psobject]
) instances containing copies of the original object's properties (but not methods), and are assigned an ETS type name of Deserialized.System.Management.Automation.SwitchParameter
, accessible via the intrinsic pstypenames
property
Therefore, the workaround code above converts such instances to [bool]
values based on the value of their .IsPresent
property, so as to make splatting work as intended.
@(...)
, the array-subexpression operator, around accessing the .Keys
property, so as to ensure that a static copy of the key collection is iterated over, which permits modification of the entries of the dictionary without provoking a Collection was modified; enumeration operation may not execute.
error.