【问题标题】:How to run PowerShell script in a different process and pass arguments to it?如何在不同的进程中运行 PowerShell 脚本并将参数传递给它?
【发布时间】:2016-12-16 14:41:50
【问题描述】:

假设我有一个脚本:

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

我设法在一个单独的进程中运行它:

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

问题是我想将参数传递给脚本,这样我就可以实现这样的目标:

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)";
}

但是-ArgumentList 在这里不起作用

我明白了:

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

我需要在不同的进程中运行 PowerShell 脚本文件的某些部分,由于 PowerShell 脚本已上传到外部系统,我无法使用其他文件。

【问题讨论】:

    标签: windows powershell process


    【解决方案1】:

    -Command 参数需要 scriptblock,您可以在其中使用 Param() 块定义参数。然后使用-args 参数传入参数。您唯一的错误是将-args 放在-command 之后 您定义脚本块之前。

    这就是它的工作原理:

    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
    

    输出:

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

    【讨论】:

    • 我已经在 PowerShell ISE 中运行了您的代码,并且脚本块被调用了两次 PS C:\Users\lixonn> 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" I am in main process 13444 I am in process 2364 Message.Status: I am here I am in process 2364 Message.Status: I am here
    • @lixonn 代码只会被调用一次,但您正在检索输出两次。您可以验证是否将GUID: $([Guid]::NewGuid()) 添加到每个Write-Host 输出。然后您将看到两个块具有相同的 GUID。但是,要修复输出,只需将执行通过管道传输到Out-Null。我将编辑我的答案以反映这一点。
    • "代码将只被调用一次,但您正在检索输出两次。"这是真的,但这是为什么呢?
    • @lixonn 这是个好问题。似乎 ISE 总是这样做,一个小例子是powershell.exe -command { Write-Host "hello"}。而 powershell 命令行似乎不是。不幸的是,我对此没有任何解释,只有Out-Null 管道的解决方法。感谢您接受答案。
    • 您的解决方案符合我的要求。这种解决方法也是可以接受的。谢谢解释
    【解决方案2】:

    您可以使用 -File 参数并在其后跟上脚本的路径。后面的任何未命名参数都将作为脚本参数传递。像下面这样的事情应该做

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

    【讨论】:

    • 感谢您的评论我忘了补充说我需要在不同的进程中运行 powershell 脚本文件的某些部分并且我不能使用另一个文件。
    • 以内容为准。例如:powershell -Command "Get-Process" "excel" 会将参数 "Excel" 传递给 cmdlet Get-Process,后者将获取任何正在运行的 excel 进程。你的背景是什么?你从哪里开始powershell?为什么不能使用脚本? (尝试将您的命令作为脚本保存到临时文件夹 - 最坏的情况)
    【解决方案3】:

    好的,如果您完全需要另一个进程但不需要另一个文件,那么您最好的选择可能是 .NET 运行空间。基本上将您的代码包装在一个脚本块中

    $SB = {
      *Your Code*
    }
    

    然后设置如下所示的运行空间,确保使用“UseNewThread”作为线程选项。请注意,$arg 是您要传递给脚本的参数是什么

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

    如果您需要在运行空间完成后从运行空间取回任何数据,您可能需要对此进行调整,但有几种方法可以做到这一点(如果您需要帮助,请发表评论)。如果您需要同步执行而不是异步,请将.BeginInvoke() 更改为.Invoke()

    【讨论】:

    • 您的解决方案看起来很有趣。我的脚本使用 Write-Host "blah blah" 那么下一个问题是 Write-Host 的输出现在会去哪里?
    • 基本上无处可去,但无论如何你都不应该在开发之外使用 write-host,使用 Write-Output 或 return 将数据返回到 shell。之后,如果您使用异步处理(.BeginInvoke()),那么您将使用$results = $psCmd.EndInvoke() 来检索结果(如果您真的想要,您可以使用Write-Host 输出)或者如果您使用.Invoke(),那么当运行空间完成后,它将返回您的数据以做任何您喜欢的事情。
    【解决方案4】:

    所以应该让你开始,但它需要一些活动部件。 首先我们定义一个新函数:

    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
    }
    

    这个函数将启动新进程。它使用start-process cmdlet,-Argumentlist 是我们应用于powershell.exe 的参数您可以删除-noExit 以使新进程在完成时关闭或添加其他powershell 标志和Start-Process 上的标志以获取根据您的要求调整窗口和行为。

    接下来我们定义我们的脚本块:

    $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)";
    }
    

    这里我们在块的开头部分定义了一些参数,它们有一个位置和名称,所以例如位置0的任何参数都将在变量$arg1中,块中的其余代码全部执行在新进程中。

    现在我们已经定义了脚本块和在新进程中运行它的函数,我们所要做的就是调用它:

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

    将此代码全部复制到您的 ISE 中,您将看到它的运行情况。

    【讨论】:

      【解决方案5】:

      Start-Job 将为它的脚本块创建一个进程,并且向它传递参数很简单。

      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)"
      

      【讨论】:

      • 仅根据您的要求,其他答案似乎过于复杂 - 我错过了什么吗?
      猜你喜欢
      • 2017-01-21
      • 2015-04-20
      • 2020-11-19
      • 2011-08-01
      • 2020-12-20
      • 2019-07-07
      • 2015-05-29
      • 1970-01-01
      相关资源
      最近更新 更多