Pass arguments to a scriptblock in powershell - powershell

I guess you can't just do this:
$servicePath = $args[0]
if(Test-Path -path $servicePath) <-- does not throw in here
$block = {
write-host $servicePath -foreground "magenta"
if((Test-Path -path $servicePath)) { <-- throws here.
dowork
}
}
So how can I pass my variables to the scriptblock $block?

Keith's answer also works for Invoke-Command, with the limit that you can't use named parameters. The arguments should be set using the -ArgumentList parameter and should be comma separated.
$sb = {
param($p1,$p2)
$OFS=','
"p1 is $p1, p2 is $p2, rest of args: $args"
}
Invoke-Command $sb -ArgumentList 1,2,3,4
Also see here and here.

A scriptblock is just an anonymous function. You can use $args inside the
scriptblock as well as declare a param block, for example
$sb = {
param($p1, $p2)
$OFS = ','
"p1 is $p1, p2 is $p2, rest of args: $args"
}
& $sb 1 2 3 4
& $sb -p2 2 -p1 1 3 4

BTW, if using the script block to run in a separate thread (multi threaded):
$ScriptBlock = {
param($AAA,$BBB)
return "AAA is $($AAA) and BBB is $($BBB)"
}
$AAA = "AAA"
$BBB = "BBB1234"
$null = Start-Job $ScriptBlock -ArgumentList $AAA,$BBB
then yields:
$null = Start-Job $ScriptBlock -ArgumentList $AAA,$BBB
Get-Job | Receive-Job
AAA is AAA and BBB is BBB1234

For anyone reading in 2020 who wants to use local variables in a remote session script block, starting in Powershell 3.0 you can use local variables directly in the scriptblock with the "$Using" scope modifier. Example:
$MyLocalVariable = "C:\some_random_path\"
acl = Invoke-Command -ComputerName REMOTEPC -ScriptBlock {Get-Acl $Using:MyLocalVariable}
Found in example 9 of https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-7

By default PowerShell won't capture variables for a ScriptBlock. You can explicitly capture by calling GetNewClosure() on it, however:
$servicePath = $args[0]
if(Test-Path -path $servicePath) <-- does not throw in here
$block = {
write-host $servicePath -foreground "magenta"
if((Test-Path -path $servicePath)) { <-- no longer throws here.
dowork
}
}.GetNewClosure() <-- this makes it work

Three example syntax:
$a ={
param($p1, $p2)
"p1 is $p1"
"p2 is $p2"
"rest of args: $args"
}
//Syntax 1:
Invoke-Command $a -ArgumentList 1,2,3,4 //PS> "p1 is 1, p2 is 2, rest of args: 3 4"
//Syntax 2:
&$a -p2 2 -p1 1 3 //PS> "p1 is 1, p2 is 2, rest of args: 3"
//Syntax 3:
&$a 2 1 3 //PS> "p1 is 2, p2 is 1, rest of args: 3"

I know this article is a bit dated, but I wanted to throw this out as a possible alternative. Just a slight variation of the previous answers.
$foo = {
param($arg)
Write-Host "Hello $arg from Foo ScriptBlock" -ForegroundColor Yellow
}
$foo2 = {
param($arg)
Write-Host "Hello $arg from Foo2 ScriptBlock" -ForegroundColor Red
}
function Run-Foo([ScriptBlock] $cb, $fooArg){
#fake getting the args to pass into callback... or it could be passed in...
if(-not $fooArg) {
$fooArg = "World"
}
#invoke the callback function
$cb.Invoke($fooArg);
#rest of function code....
}
Clear-Host
Run-Foo -cb $foo
Run-Foo -cb $foo
Run-Foo -cb $foo2
Run-Foo -cb $foo2 -fooArg "Tim"

Other possibility:
$a ={
param($p1, $p2)
"p1 is $p1"
"p2 is $p2"
"rest of args: $args"
};
$a.invoke(1,2,3,4,5)

Related

Pass command line arguments on to to "self" but with Start-Job

At [Asynchronous start][1] I had a question about starting a power-shell script asynchronously which creates a form. As answered in that question this can be solved using start-job
eg
Start-Job -ScriptBlock { test NW -NoWarning -Paranoia:2 }
So I have tried to write the Test.PS1 script routine so it re-calls itself with "Start-Job Test -NoSpawn" The switch nospawn then means it runs without a second call. I have tested this with the example code the above line now has to be and it works
Start-Job -ScriptBlock { test NW -NoSpawn -NoWarning -Paranoia:2 }
However I'm struggling to get the parameters from the original command line to passthrough to the job
I have tried creating a string in the correct format , an array , list the arguments manually , I either get repeated arguments being passed or all of the string ending up in the first Parameter $ComputerList -
A summary of the parameters and the attempts are
Param ([string]$ComputerList = 'status\edi.csv',[switch]$NoSpawn,[switch]$NoWarning,[switch]$Debug,[INT]$Paranoia=6)
...... <Snip>
Start-Job -ScriptBlock { test $ComputerList -NoSpawn -NoWarning:$NoWarning -Paranoia:$Paranoia }
Doesn't work due to scope - also switches are wrong way to do this
Start-Job -ScriptBlock { test -NoSpawn $Args } -argumentlist $ComputerList
Insufficent arguments but works - But I think One Argument is possible ?
Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist #("-NoWarning:$NoWarning","-ComputerList:$ComputerList","-Paranoia:$Paranoia")
Everything ends up in $ComputerList
Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist "-NoWarning:$NoWarning -ComputerList:$ComputerList -Paranoia:$Paranoia"
Everything ends up in $ComputerList
Full code follows
Param ([string]$ComputerList = 'status\edi.csv',[switch]$NoSpawn,[switch]$NoWarning,[switch]$Debug,[INT]$Paranoia=6)
$Log_Paranoia=$Paranoia
If ($Debug) { $debugPreference="Continue"} #enable debug messages if -debug is specified
If ($NoWarning) { $WarningPreference="SilentlyContinue"} #turn off warning messages
function Write-Paranoid($Level, $message) {
$CS=Get-PSCallStack
$Caller = $CS[1]
$Module = "$($Caller.FunctionName)[$($Caller.ScriptLineNumber)]"
$Diff=$level - $Log_Paranoia
$MSG= "$Module($($Level),$($Log_Paranoia)):$message"
if ($level - $Log_Paranoia -le 0 ) {
Write-host $MSG
}
if($Error.Count -gt 0 ) {
$MSG= "$Module($Level)ERROR:$Error[0]"
Write-Error $MSG
}
$error.clear()
}
Function AddStatusBar($form , $Txt) {
Write-Paranoid 10 "Enter"
$statusBar = New-Object System.Windows.Forms.StatusBar
$statusBar.DataBindings.DefaultDataSourceUpdateMode = 0
$statusBar.TabIndex = 4
$statusBar.Size = SDS 428 22
$statusBar.Location = SDP 0 337
$statusBar.Text = $Txt
$form.Controls.Add($statusBar)
$statusBar
Write-Paranoid 10 "Exit"
}
Function Create-Form ($Title)
{
Write-Paranoid 10 "Enter"
$form1 = New-Object System.Windows.Forms.Form
$form1.Text = $Title
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$form1.ClientSize = SDS 890 359
$form1.StartPosition = 0
$form1.BackColor = [System.Drawing.Color]::FromArgb(255,185,209,234)
$form1
Write-Paranoid 10 "Exit"
}
Function GenerateTestForm
{
Write-Paranoid 10 "Enter"
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
$Form1 = Create-Form "Test Form"
$Alist = Get-CommandLine
$StatusBar = AddStatusBar $form1 $AList
$form1.ShowDialog() | Out-Null # Suspends calller
Write-Paranoid 10 "Exit"
}
if ($NoSpawn )
{
Write-Paranoid 3 " NoSpawn "
Write-Paranoid 5 "Call GenerateForm"
if ($Test) {
GenerateTestForm
} else {
GenerateTestForm
}
} else {
Write-Paranoid 3 "NOT NoSpawn restarting as job"
# note that test.ps1 is in the path so it will restart this script
# Start-Job -ScriptBlock { test $ComputerList -NoSpawn -NoWarning:$NoWarning -Paranoia:$Paranoia } #Wrong scope
# Start-Job -ScriptBlock { test -NoSpawn $Args } -argumentlist $ComputerList # Insufficent aruments but works - ONLY One Argument possible -
# Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist #("-NoWarning:$NoWarning","-ComputerList:$ComputerList","-Paranoia:$Paranoia") # Everything ends up in $ComputerList
# Start-Job -ScriptBlock { test $Args -NoSpawn } -argumentlist "-NoWarning:$NoWarning -ComputerList:$ComputerList -Paranoia:$Paranoia" # Everything ends up in $ComputerList
}
Your problem can be reduced to this:
How can I re-invoke the script at hand as a background job, passing all original arguments (parameter values, including default parameter values) through?
A simplified example:
param (
[string] $ComputerList = 'status\edi.csv',
[switch] $NoSpawn,
[switch] $NoWarning,
[switch] $Debug,
[int] $Paranoia=6
)
if ($NoSpawn) { # already running as a background job.
"I'm now running in the background with the following arguments:"
$PSBoundParameters
} else { # must re-invoke via a background job
# Add *default* parameter values, if necessary, given that
# they're *not* reflected in $PSBoundParameters.
foreach ($paramName in $MyInvocation.MyCommand.Parameters.Keys) {
if (-not $PSBoundParameters.ContainsKey($paramName)) {
$defaultValue = Get-Variable -Scope Local -ValueOnly $paramName
if (-not ($null -eq $defaultValue -or ($defaultValue -is [switch] -and -not $defaultValue))) {
$PSBoundParameters[$paramName] = $defaultValue
}
}
}
# Start a background job that reinvokes this script with the original
# arguments / default values.
Start-Job {
$argsHash = $using:PSBoundParameters
& $using:PSCommandPath -NoSpawn #argsHash
} |
Receive-Job -Wait -AutoRemoveJob
}
Note:
For demonstration purposes, the initial call waits for the re-invocation via a background job to finish, using Receive-Job -Wait -AutoRemoveJob
In your real code, you can simply discard Start-Job's output (a job-information object) with $null = Start-Job { ... }, and then rely on the job getting cleaned up when the caller's session as a whole exits.
The extra code needed to propagate parameter default values is somewhat cumbersome, but necessary, given that the automatic $PSBoundParameters variable does not reflect default values.
GitHub issue #3285 discusses this limitation, and suggests a potential future solution.

Send string parameters to a Start-Job script block

I need to initialize a job using the shell. The job will be a delay plus a call to a vbScript. The following code works fine. For my example, the vbScript is just a single line with a MsgBox "Hello world!"
$functions = {
Function execute_vbs {
param ([string]$path_VBScript, [int]$secs)
Start-Sleep -Seconds $secs
cscript /nologo $path_VBScript
}
}
$seconds = 2
Start-Job -InitializationScript $functions -ScriptBlock {execute_vbs -path_VBScript 'C:\Users\[USERNAME]\Desktop\hello_world.vbs' -secs $seconds} -Name MyJob
The problem comes the moment I want to parameterize the vbScript path. (the idea is to do several different calls to some different vbScripts).
When I do this, the command seems to ignore the parameter input. I did other tests with int parameter and they work fine, the problem looks to be only with the string parameters. The following code does not work:
$functions = {
Function execute_vbs {
param ([string]$path_VBScript, [int]$secs)
Start-Sleep -Seconds $secs
cscript /nologo $path_VBScript
}
}
$input = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
$seconds = 2
Start-Job -InitializationScript $functions -ScriptBlock {execute_vbs -path_VBScript $input -secs $seconds} -Name MyJob
I've also tried using the [-ArgumentList] command, but it has the same problem.
Any idea?
The problem is that the $input and $seconds variables inside your script block are in a different scope and are effectively different variables to the ones in the main script.
I've modified your script slightly to remove the call to VBScript to make it easier to reproduce here - my example code is:
$functions = {
Function execute_vbs {
param ([string]$path_VBScript, [int]$secs)
Start-Sleep -Seconds $secs
write-output "filename = '$path_VBScript'"
write-output "secs = '$secs'"
}
}
Here's two ways to fix it:
The Using: scope modifier
See https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7#the-using-scope-modifier for the full details, but basically:
For any script or command that executes out of session, you need the Using scope modifier to embed variable values from the calling session scope, so that out of session code can access them.
$filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
$seconds = 2
$job = Start-Job -InitializationScript $functions -ScriptBlock {
execute_vbs -path_VBScript $using:filename -secs $using:seconds
} -Name MyJob
wait-job $job
receive-job $job
# output:
# filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
# secs = '2'
Note the $using before the variable names inside the script block - this allows you to "inject" the variables from your main script into the scriptblock.
ScriptBlock Parameters
You can define parameters on the script block similar to how you do it with a function, and then provide the values in the -ArgumentList parameter when you invoke Start-Job.
$filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
$seconds = 2
$job = Start-Job -InitializationScript $functions -ScriptBlock {
param( [string] $f, [int] $s )
execute_vbs -path_VBScript $f -secs $s
} -ArgumentList #($filename, $seconds) -Name MyJob
wait-job $job
receive-job $job
# output:
# filename = 'C:\Users\[USERNAME]\Desktop\hello_world.vbs'
# secs = '2'
``

Passing array to one of named parameters in script block?

I am trying to figure out a way to pass an array to a named parameter in a separate script. However, I could not find any solution.
Test2.ps1:
param(
[int]$a,
[int]$b,
[int]$c,
[string[]]$d
)
write-host "`$a = $a"
write-host "`$b = $b"
write-host "`$c = $c"
write-host "`$d:" -nonewline
foreach($str in $d) {
write-host " $str" -nonewline
}
write-host
Main script:
$arr = #("abc", "def", "ghi")
$params = #{
a = 1;
b = 2;
c = 3;
d = $arr
}
#invoke-command -filepath "test2.ps1" -ArgumentList 1,2,3,#("abc", "def", "ghi")
$scriptPath = "test2.ps1"
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} #params)")
invoke-command -scriptblock $sb
When executed, I got output that
$d:System.Object[]
The line below was copied from another Stackoverflow answer but I don't quite understand how it worked for the first 3 named parameters.
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} #params)")
Especially the "$(&{$args} #params)" part. I have basic understanding of splatting, but this is beyond me. Much appreciated if someone can explain the syntax a bit for me.
When you put #params inside an expandable string, you're forcing the parser to convert the resulting output to a string, and the default behavior of ToString() (if not overridden) is to just return the type name of the object in question.
Simply wait until you call the script before you supply the parameters:
$sb = [scriptblock]::Create("$(get-content $ScriptPath -Raw)")
& $sb $args #params
or, if you want to dot-source the script with specific parameters:
$sb = [scriptblock]::Create("$(get-content $ScriptPath -Raw)")
& {.$sb $args #params}

PowerShell Pass Named parameters to ArgumentList

I have a PowerShell script that accepts 3 named parameters. Please let me know how to pass the same from command line. I tried below code but same is not working. It assigns the entire value to P3 only. My requirement is that P1 should contain 1, P2 should 2 and P3 should be assigned 3.
Invoke-Command -ComputerName server -FilePath "D:\test.ps1" -ArgumentList {-P1 1 -P2 2 -P3 3}
Ihe below is script file code.
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value :" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value :" $P3
One option:
$params = #{
P1 = 1
P2 = 2
P3 = 3
}
$ScriptPath = 'D:\Test.ps1'
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} #params)")
Invoke-Command -ComputerName server -ScriptBlock $sb
The code by mjolinor works great, but it took me several minutes to understand it.
The code makes a simple thing - generates a content of script block with built-in parameters:
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
Then this script block is passed to Invoke-Command.
To simplify the code:
".{$(get-content $ScriptPath -Raw)} $(&{$args} #params)"
$scriptContent = Get-Content $ScriptPath -Raw
$formattedParams = &{ $args } #params
# The `.{}` statement could be replaced with `&{}` here, because we don't need to persist variables after script call.
$scriptBlockContent = ".{ $scriptContent } $formattedParams"
$sb = [scriptblock]::create($scriptBlockContent)
Let's make a basic C# implementation:
void Run()
{
var parameters = new Dictionary<string, string>
{
["P1"] = "1",
["P2"] = "2",
["P3"] = "3"
};
var scriptResult = InvokeScript("Test.ps1", "server", parameters)
Console.WriteLine(scriptResult);
}
string InvokeScript(string filePath, string computerName, Dictionary<string, string> parameters)
{
var innerScriptContent = File.ReadAllText(filePath);
var formattedParams = string.Join(" ", parameters.Select(p => $"-{p.Key} {p.Value}"));
var scriptContent = "$sb = { &{ " + innerScriptContent + " } " + formattedParams + " }\n" +
$"Invoke-Command -ComputerName {computerName} -ScriptBlock $sb";
var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".ps1");
File.WriteAllText(tempFile, scriptContent);
var psi = new ProcessStartInfo
{
FileName = "powershell",
Arguments = $#"-ExecutionPolicy Bypass -File ""{tempFile}""",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(psi);
var responseText = process.StandardOutput.ReadToEnd();
File.Delete(tempFile);
return responseText;
}
The code generates a temporary script and executes it.
Example script:
$sb = {
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
}
Invoke-Command -ComputerName server -ScriptBlock $sb
Here's a simple solution:
[PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(#{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
Here's output:
PS C:\Windows\system32> [PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(#{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
P1 Value :
1
P2 Value:
2
P3 Value :
3
If you are trying to use the -FilePath with named parameters (-P1 1 -P2 2), then I found this will work. Use a script block to run the file, instead of the using -FilePath.
Invoke-Command -ComputerName server -ScriptBlock {& "D:\test.ps1" -P1 1 -P2 2 -P3 3}
Use a hashtable :
icm -ComputerName test -ScriptBlock{$args} -ArgumentList #{"p1"=1;"p2"=2;"p3"=3}
Use a hashtable, indeed!
#TestPs1.ps1
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value :" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value :" $P3
$params = #{
P3 = 3
P2 = 2
}
#(just to prove it doesn't matter which order you put them in)
$params["P1"] = 1;
#Trhough the use of the "Splat" operator, we can add parameters directly onto the module
& ".\TestPs1.ps1" #params
outputs:
P1 Value :
1
P2 Value:
2
P3 Value :
3
If you're willing to skip Invoke-Command altogether...
Your script could look like this:
([string]$args).split('-') | %{
if ($_.Split(' ')[0].ToUpper() -eq "P1") { $P1 = $_.Split(' ')[1] }
elseif ($_.Split(' ')[0].ToUpper() -eq "P2") { $P2 = $_.Split(' ')[1] }
elseif ($_.Split(' ')[0].ToUpper() -eq "P3") { $P3 = $_.Split(' ')[1] }
}
Write-Output "P1 Value :" $P1
Write-Output "P2 Value :" $P2
Write-Output "P3 Value :" $P3
And you would call it like this:
D:\test.ps1 -P1 1 -P2 2 -P3 3

Powershell: passing parameters to functions stored in variables

I'm trying to get a simple working example of using functions inside of jobs. I've managed to pass my function into the scriptblock used for my job, but I can't seem to get parameters to the function.
# concurrency
$Logx =
{
param(
[parameter(ValueFromPipeline=$true)]
$msg
)
Write-Host ("OUT:"+$msg)
}
# Execution starts here
cls
$colors = #("red","blue","green")
$colors | %{
$scriptBlock =
{
Invoke-Expression -Command $args[1]
Start-Sleep 3
}
Write-Host "Processing: " $_
Start-Job -scriptblock $scriptBlock -args $_, $Logx
}
Get-Job
while(Get-Job -State "Running")
{
write-host "Running..."
Start-Sleep 2
}
# Output
Get-Job | Receive-Job
# Cleanup jobs
Remove-Job *
Here's the output:
Processing: red
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
175 Job175 Running True localhost ...
Processing: blue
177 Job177 Running True localhost ...
Processing: green
179 Job179 Running True localhost ...
179 Job179 Running True localhost ...
177 Job177 Running True localhost ...
175 Job175 Running True localhost ...
Running...
Running...
OUT:
OUT:
OUT:
So as evidenced by the OUT: x3 in the output my function is getting called, but I haven't found any syntax that allows me to get the parameter to the function. Thoughts?
EDIT:
Note in Shawn's observation below and my response I tried using functions as variables because using a traditional function does not seem to work. If there is a way to get that working I'd be more than happy to not have to pass my functions around as variables.
The answer is to use the initializationscript parameter of Start-Job. If you define all your functions in a block and pass the block they become available.
Solution was found in this post:
How do I Start a job of a function i just defined?
Here is my example from before, now working:
# concurrency
$func = {
function Logx
{
param(
[parameter(ValueFromPipeline=$true)]
$msg
)
Write-Host ("OUT:"+$msg)
}
}
# Execution starts here
cls
$colors = #("red","blue","green")
$colors | %{
$scriptBlock =
{
Logx $args[0]
Start-Sleep 9
}
Write-Host "Processing: " $_
Start-Job -InitializationScript $func -scriptblock $scriptBlock -args $_
}
Get-Job
while(Get-Job -State "Running")
{
write-host "Running..."
Start-Sleep 2
}
# Output
Get-Job | Receive-Job
# Cleanup jobs
Remove-Job *
If you do not prefix your function name with keyword function, PowerShell does not know to treat it as such. As you have written your script it is basically a variable with some special text in it. Which as your output shows it is only executing the commands it recognizes within that variable's content: Write-Host "OUT:".
Using the correct syntax will tell PowerShell it is a function and that you have variables to pass into it that you need executed:
function Logx
{
param(
[parameter(ValueFromPipeline=$true)]
$msg
)
Write-Host ("OUT:"+$msg)
}
Then when you call it within your script you will just use Logx
Got this far. Have to run out, will try back later.
PS: What is getting passed at args[1], I am getting a lot of red,
CategoryInfo : InvalidData: (:) [Invoke-Expression], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.InvokeExpressionCommand
here is what I've managed so far.
# concurrency
$Logx =
{
param(
[parameter(ValueFromPipeline=$true)]
$msg
)
Write-Host ("OUT:"+$msg)
}
# Execution starts here
cls
$colors = #("red","blue","green")
$colors | %{
& $scriptBlock =
{ Invoke-Expression -Command $args[1]
Start-Sleep 3
}
Write-Host "Processing: " $_
Start-Job -scriptblock $scriptBlock -ArgumentList #($_, $Logx)
}
# Get-Job
while(Get-Job -State "Running")
{
write-host "Running..."
Start-Sleep 2
}
# Output
Get-Job | Receive-Job
# Cleanup jobs
Remove-Job *