简体   繁体   中英

How to run PowerShell script in a different process and pass arguments to it?

Let's say I have a script:

write-host "Message.Status: Test Message Status";

I managed to run it in a separate process by doing:

powershell.exe -Command
{ write-host "Message.Status: Test Message Status"; }  

The problem is I want to pass parameters to the script so that I can achieve something like this:

write-host "I am in main process"
powershell.exe -Command -ArgumentList "I","am","here"
{
    write-host "I am in another process"
    write-host "Message.Status: $($one) $($two) $($three)";
}

However -ArgumentList doesn't work here

I get:

powershell.exe : -ArgumentList : The term '-ArgumentList' is not recognized as the name of a cmdlet, function, script file, or operable 

I need to run some part of PowerShell script file in a different process and I cannot use another file due to the fact that PowerShell script is uploaded to external system.

The -Command parameter is expecting a scriptblock in which you can define your parameters using a Param() block. Then use the -args parameter to pass in the arguments. Your only mistake was to put the -args after -command before you defined the scriptblock.

So this is how it works:

write-host "I am in main process $($pid)"
powershell.exe -Command {
    Param(
        $one,
        $two,
        $three
    )
    write-host "I am in process $($pid)"
    write-host "Message.Status: $($one) $($two) $($three)";
} -args "I", "am", "here" | Out-Null

Output:

I am in main process 17900
I am in process 10284
Message.Status: I am here

You can use the -File parameter and follow it by the path to script. Any unnamed arguments which follows will be passed as script parameters. Something like below should do

powershell -File "C:\ScriptFolder\ScriptwithParameters.ps1" "ParameterOneValu" "valuetwo"

Ok so if you need another process entirely but not another file then your best bet is probably .NET runspaces. Basically wrap your code in a scriptblock

$SB = {
  *Your Code*
}

Then set up a runspace like below, making sure to use the "UseNewThread" as the thread option. Note that $arg is whatever your argument to be passed to the script is

$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "UseNewThread"         
$newRunspace.Open()
$psCmd = [PowerShell]::Create().AddScript($SB).AddArgument($arg)
$psCmd.Runspace = $newRunspace
$data = $psCmd.BeginInvoke()

You'll likely need to tweak this if you need to get any data back from the runspace once it is complete but there are a few ways to do that(leave a comment if you need assistance). If you need synchronous execution rather than async then change .BeginInvoke() to .Invoke()

So should get you started, But it will require a few moving parts. First we define a new function:

function Run-InNewProcess{
  param([String] $code)
  $code = "function Run{ $code }; Run $args"
  $encoded = [Convert]::ToBase64String( [Text.Encoding]::Unicode.GetBytes($code))

  start-process PowerShell.exe -argumentlist '-noExit','-encodedCommand',$encoded
}

This function will be what starts the new process. It uses the start-process cmdlet, The -Argumentlist is our arguments applied to the powershell.exe You can remove -noExit to make the new process close on completion or add other powershell flags, and flags on Start-Process to get the windows and behaviours tweaked to your requirements.

Next we define our script block:

$script = {
    [CmdletBinding()]
    Param (
    [Parameter(Position=0)]
    [string]$Arg1,

    [Parameter(Position=1)]
    [string]$Arg2)

    write-host "I am in another process"
    write-host "Message.Status: $($Arg1) $($Arg2)";
}

Here we define some parameters in the opening part of the block, They have a position and name, so for example any argument in position 0 will be in the variable $arg1 The rest of the code in the block is all executed in the new process.

Now we have defined the script block and the function to run it in a new process, All we have to do is call it:

Run-InNewProcess $script -Arg1 '"WHAT WHAT"' -Arg2 '"In the But"'

Copy past this code all in to your ISE and you will see it in action.

Start-Job will create a process for its scriptblock, and it's straightforward to pass arguments to it.

Write-Host "Process count before starting job: $((Get-Process |? { $_.ProcessName -imatch "powershell" }).Count)"

$job = Start-Job `
    -ArgumentList "My argument!" `
    -ScriptBlock { 
        param($arg) 
        Start-Sleep -Seconds 5; 
        Write-Host "All Done! Argument: $arg" 
    }

while ($job.State -ne "Completed")
{
    Write-Host "Process count during job: $((Get-Process |? { $_.ProcessName -imatch "powershell" }).Count)"
    Start-Sleep -Seconds 1
}
Receive-Job $job -AutoRemoveJob -Wait
Write-Host "Process count after job: $((Get-Process |? { $_.ProcessName -imatch "powershell" }).Count)"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM