Reputation: 1491
I'm trying to create a background job, which executes a scriptblock. I need to pass this scriptblock in as a parameter, but I can't seem to get the syntax to work. The scriptblock is being converted to a string somewhere along the way.
It works fine when I pass the script block to a local function, but not through start-job
The following syntax works:
function LocalFunction
{
param (
[parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock
)
&$ScriptBlock | % { echo "got $_" }
}
LocalFunction -ScriptBlock { echo "hello" }
This outputs "got hello" as expected.
But the following fails:
$job = start-job -argumentlist { echo "hello" } -scriptblock {
param (
[parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock
)
&$ScriptBlock | % { echo "got $_" }
}
start-sleep -s 1
receive-job $job
The error it returns is
Receive-Job : Cannot process argument transformation on parameter 'ScriptBlock'. Cannot convert the " echo "hello" " value of type "System.String" to type "System.Management.Automation.ScriptBlock".
So if I'm reading the error right, it appears that -argumentlist is somehow forcing its arguments into strings.
Upvotes: 25
Views: 37171
Reputation: 131
So if your desire is to insert an inline scriptblock, then Shay's solution (as noted) is probably the best. On the other hand if you simply want to pass a scriptblock as a parameter consider using a variable of type scriptblock
and then passing that as the value of the -ScriptBlock
parameter.
function LocalFunction
{
param (
[parameter(Mandatory=$true)]
[ScriptBlock]$ScriptBlock
)
&$ScriptBlock | % { echo "got $_" }
}
[scriptblock]$sb = { echo "hello" }
LocalFunction -ScriptBlock $sb
Upvotes: 1
Reputation: 2333
Looks like this works today.
function LocalFunction
{
param
(
[scriptblock] $block
)
$block.Invoke() | % { "got $_" }
}
LocalFunction { "Hello"}
Upvotes: 6
Reputation: 11
You have to read it in as a string and then convert it to a scriptblock.
In powershell v1 you can do this:
$ScriptBlock = $executioncontext.invokecommand.NewScriptBlock($string)
And in powershell v2 you can do this:
$ScriptBlock = [scriptblock]::Create($string)
So your code would look like this:
function LocalFunction
{
param (
[parameter(Mandatory=$true)]
$ScriptBlock
)
$sb = [scriptblock]::Create($ScriptBlock)
$sb | % { echo "got $_" }
}
LocalFunction -ScriptBlock "echo 'hello'"
The '[scriptblock]::Create($ScriptBlock)' will place the curly braces around the string for you creating the script block.
Found the info here http://get-powershell.com/post/2008/12/15/ConvertTo-ScriptBlock.aspx
Upvotes: 1
Reputation: 11651
Based on my experiments, PowerShell is parsing -ArgumentList, which is an Object[], as a string, even when you pass in a script block. The following code:
$job = start-job -scriptblock { $args[0].GetType().FullName } -argumentlist { echo "hello" }
start-sleep -s 1
receive-job $job
results in the following output:
System.String
As far as I know, the only solution here is Shay's, though you don't need to pass in the -ArgumentList as a string as PowerShell will parse your script block as a string in this case.
Upvotes: 4
Reputation: 126722
Here's one way to solve this, pass the scriptblock code as a string, then create a scriptblock from the string inside the job and execute it
Start-Job -ArgumentList "write-host hello" -scriptblock {
param (
[parameter(Mandatory=$true)][string]$ScriptBlock
)
& ([scriptblock]::Create($ScriptBlock))
} | Wait-Job | Receive-Job
Upvotes: 23