Reputation: 6863
I have a function that returns a complex nested hash table data structure, where part of it forms the arguments for a further function call, and part of it is strings for reporting errors that result in the arguments not being populated. Ideally I would like to splat just the arguments hash table, but I am starting to think that can't be done. So, an example of the basic problem looks like this...
function Test {
param (
[String]$A,
[String]$B,
[String]$C
)
Write-Host "A: $A"
Write-Host "B: $B"
Write-Host "C: $C"
}
$data = @{
arguments = @{
A = 'A string'
B = 'Another string'
C = 'The last string'
}
kruft = 'A string that doesn not need to get passed'
}
Ideally I want to splat $data.arguments
, so something like this...
Test @data.arguments
But that doesn't work, resulting in the error
The splatting operator '@' cannot be used to reference variables in an expression. '@data' can be used only as an argument to a command. To
reference variables in an expression use '$data'.
So I tried...
Test @(data.arguments)
Which results in the error
data.arguments : The term 'data.arguments' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path is correct and try again.
I also tried...
Test @($data.arguments)
Which results in the whole hash table being passed as a single argument and the output is
A: System.Collections.Hashtable
B:
C:
What DOES work is...
$arguments = $data.arguments
Test @arguments
Which has me thinking you really cannot splat anything but a simple variable that is an appropriate hash table. But, I am hoping someone can verify that is indeed true, or point out the solution I haven't come up with yet. The actual code requires 5 arguments, with somewhat verbose names because I prefer descriptive names, so splatting is very much an appropriate solution. Needing to make a new variable with just the hash table to be passed isn't an issue, just wondering if it really is the only option.
Upvotes: 0
Views: 1916
Reputation: 1
It is possible by passing the object property to the ForEach-Object cmdlet:
New-Variable -Name objTask -Value (New-Object PSCustomObject -ArgumentList @{
TaskName = "Task Name"
TaskPath = '\Task\Path\'
Description = "Describe the function of this task"
Parameters = @{
Principal = @{
UserId = "S-1-5-18"
LogonType = "ServiceAccount"
RunLevel = "Highest"
}
Triggers = @(
@{
Daily = $true
At = ([datetime]"01/01/2015 10:00:00")
DaysInterval = 1
},
@{
Daily = $true
At = ([datetime]"01/01/2015 14:00:00")
DaysInterval = 1
}
)
Actions = @{
Execute = "powershell.exe"
Argument = "-ep bypass -c `"myscript.ps1`""
}
Settings = @{
MultipleInstances = "IgnoreNew"
}
}
}
$objTask | ForEach-Object {
$stPrincipal = $_.Parameters.Principal | ForEach-Object {
New-ScheduledTaskPrincipal @_
}
$stTriggers = $_.Parameters.Triggers | ForEach-Object {
New-ScheduledTaskTrigger @_
}
$stActions = $_.Parameters.Actions | ForEach-Object {
New-ScheduledTaskAction @_
}
$stSettings = $_.Parameters.Settings | ForEach-Object {
New-ScheduledTaskSettingsSet @_
}
$stTask = New-ScheduledTask `
-Principal $stPrincipal `
-Trigger $stTriggers `
-Action $stActions `
-Settings $stSettings `
-Description $_.Description
$myTask = $stTask | Register-ScheduledTask `
-TaskName $_.TaskName `
-TaskPath $_.TaskPath `
-Force `
-ErrorAction SilentlyContinue
}
Upvotes: 0
Reputation: 34168
This is a totally artificial way (more of a hack) of doing this but you could pass it as a hash table then self-call with the arguments splatted
function Test {
param (
[Parameter()]
[hashtable]$ht,
[String]$A,
[String]$B,
[String]$C
)
if($null -eq $ht){
Write-Host "A: $a"
Write-Host "B: $B"
Write-Host "C: $C"
}
else
{
Write-Host "ht.A: $($ht.a)"
Test -a $($ht.A+ " new blob") -b $ht.B -c $ht.C
Test @ht
}
}
$tdata = @{
arguments = @{
A = 'A string'
B = 'Another string'
C = 'The last string'
}
kruft = 'A string that doesn not need to get passed'
}
Write-Host 'Test ht' -ForegroundColor Yellow
Test -ht $tdata.arguments
Upvotes: 0
Reputation: 864
That's not possible.
Any variable (and only variables) provided with "@" instead of "$" for a function parameter is declared as TokenKind "SplattedVariable".
PS: Some quick tests from my side which could have succeed (apart from PS design):
Write-Host 'Test 1' -ForegroundColor Yellow
Test @$data.arguments
Write-Host 'Test 2' -ForegroundColor Yellow
Test @$($bla = $data.arguments)
Write-Host 'Test 3' -ForegroundColor Yellow
Test @$bla = $data.arguments
Write-Host 'Test 4' -ForegroundColor Yellow
Test @$bla = $data.arguments.GetEnumerator()
Write-Host 'Test 5' -ForegroundColor Yellow
Test @$($data.arguments.GetEnumerator())
... but they didn't.
Upvotes: 1
Reputation: 22821
I cannot claim to fully understand what you're attempting, but here are a couple of solutions that might help.
Your Test
function is expecting three strings, but I don't see anything in your example that satisfies that. I would either change it to accept a Hashtable
(which is the data type in question) or have it accept a string array and pass $data.arguments.values
Using a Hashtable
:
function Test {
param (
[Hashtable]$hash
)
write-host $hash.A
write-host $hash.B
write-host $hash.C
}
Test $data.arguments
Using a String
array:
function Test {
param (
[String[]]$a
)
$a | % { write-host $_ }
}
Test $data.arguments.values
Upvotes: 1