Consider the following simple function:
function Write-HostIfNotVerbose()
{
if ($VerbosePreference -eq 'SilentlyContinue')
{
Write-Host @args
}
}
And it works fine:
Now I want to make it an advanced function, because I want it to inherit the verbosity preference:
function Write-HostIfNotVerbose([Parameter(ValueFromRemainingArguments)]$MyArgs)
{
if ($VerbosePreference -eq 'SilentlyContinue')
{
Write-Host @MyArgs
}
}
But it does not work:
And what drives me nuts is that I am unable to identify how $args in the first example is different from $args in the second.
I know that the native @args splatting does not work for advanced functions by default - https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting?view=powershell-7.2#notes
But I hoped it could be simulated, yet it does not work either. My question is - what is wrong with the way I am trying to simulate it and whether it is possible to fix my code without surfacing all the Write-Host parameters at Write-HostIfNotVerbose


Santiago Squarzon's helpful answer contains some excellent sleuthing that reveals the hidden magic behind
@args, i.e. splatting using the automatic$argsvariable, which is available in simple (non-advanced) functions only.The solution in Santiago's answer isn't just complex, it also isn't fully robust, as it wouldn't be able to distinguish
-ForegroundColor(a parameter name) from'-ForegroundColor'a parameter value that happens to look like a parameter name, but is distinguished from it by quoting.@argsmagic has a limitation: it doesn't correctly pass a[switch]parameter specified with an explicit value through, such as-NoNewLine:$false[1]A robust solution requires splatting via the automatic
$PSBoundParametersvariable, which in turn requires that the wrapping function itself also declare all potential pass-through parameters.Such a wrapping function is called a proxy function, and the PowerShell SDK facilitates scaffolding such functions via the PowerShell SDK, as explained in this answer.
In your case, you'd have to define your function as follows:
[1] Such an argument is invariably passed through as two arguments, namely as parameter name
-NoNewLineby itself, followed by a separate argument,$false. The problem is that at the time the original arguments are parsed into$args, it isn't yet known what formally declared parameters they will bind to. TheNotePropertytagging applied to$argsfor marking elements as parameter names doesn't preserve the information as to whether the subsequent argument was separated from the parameter name with:, which for a[switch]parameter is necessary to identify that argument as belonging to the switch. In the absence of this information, two separate arguments are always passed during splatting.