Powershell 启动作业范围

Powershell start-job scope

我的剧本很长。我有一个记录功能:

function Log ([string]$Content){
    $Date = Get-Date
    Add-Content -Path $LogPath -Value ("$Date : $Content")
}

在脚本的某个时刻,我需要并行 运行 个作业。 我有一个计算机名称列表,我需要对每个计算机名称使用 psexec。这应该作为 运行 并行

的工作来完成

        Start-Job -ScriptBlock {
        
        Log "$line Has called"
            $Program_List = gc $USERS_DB_PATH$line.txt | select -Skip 1
            if (Test-Connection $line -Quiet) {
                    ForEach ($program in $Program_List){
                    Log "$line $program"
                    #"line $line bot is $bot pwd is $pwd"
                    psexec \"$line" -u bla.local\"$bot" -p $pwd cmd bla
                    
                    }
            }
            else{
                  Log "Cannot Connect to $line"
            }
        
        
        }
        #Remove-Item "$USERS_DB_PATH$line.txt"


}

我知道这与 Scope 有关,但我怎样才能让这个脚本块看到函数 Log 和所有必要的变量?他们都空了

tl;dr

  • 通过$using:作用域从调用者作用域引用变量。

  • 在后台作业的上下文中重新创建您的Log函数,使用$function:Log = $using:function:Log

Start-Job -ScriptBlock {

  # Required in Windows PowerShell only (if needed).
  # Change to the same working directory as the caller.
  Set-Location -LiteralPath ($using:PWD).ProviderPath

  # Recreate the Log function.
  $function:Log = $using:function:Log
        
  # All variable values from the *caller*'s scope must be $using: prefixed.
  Log "$using:line Has called"
  # ...        
        
}
  • 继续阅读以获得解释。

  • 请参阅底部部分,了解 Start-Job 的更好替代方案:Start-ThreadJobForEach-Object -Parallel(仅限 PowerShell(核心)7+)。


不可见的 PowerShell 子进程 中的后台作业 运行,即单独的 powershell.exe ( Windows PowerShell) pwsh (PowerShell (Core) 7+) 进程。

这样的子进程:

  • 不加载 $PROFILE 个文件。
  • 对调用者的状态一无所知;也就是说,它无权访问调用者在会话中定义的变量、函数、别名……; 只有环境变量是从调用者.
  • 继承的

相反,这意味着只有以下命令在后台作业中默认可用:

  • 外部程序和 *.ps1 脚本,通过 $env:PATH 环境变量中列出的目录。
  • 模块中的命令可通过 module-autoloading feature,来自 $env:PSModulePath 环境变量(具有默认模块)中列出的目录。

将调用者状态信息传递给后台作业:

  • 变量:

    • 虽然您不能将变量 本身 传递给后台作业,但您可以传递它们的 ,使用 $using:范围;换句话说:您可以 获取 的值,但不能 更新 调用方范围内的变量 - 请参阅概念性 about_Remote_Variables.

    • 或者,通过 Start-Job's -ArgumentList (-Args) parameter, which the -ScriptBlock argument must then access in the usual manner: either via the automatic $args variable or via explicitly declared parameters, using a param() block.

      将值作为 参数 传递
  • 函数:

    • 类似地,你不能像这样传递函数,而只能传递函数的body,这是最简单的方法要做到这一点是通过 ;例如要获取函数体 foo,请使用 $function:foo;要将其传递给后台作业(或远程调用),请使用 $using:function:foo.

    • 由于命名空间变量符号也可用于 分配 值,分配给 $function:foo 会创建或更新名为 foo 的函数, 以便 $function:foo = $using:function:foo 在后台会话中有效地重新创建一个 foo 函数。

      • 请注意,虽然 $function:foo returns 函数体作为 [scriptblock] 实例,$using:function:foo 变成了 字符串 在序列化期间(参见 GitHub issue #11698;不过,幸运的是,您还可以从字符串创建函数体。
  • 工作目录:

    • Windows PowerShell后台作业使用固定工作目录:用户Documents 文件夹。为确保后台作业使用与调用者相同的目录,调用
      Set-Location -LiteralPath ($using:PWD).ProviderPath 作为传递给 -ScriptBlock.

      的脚本块内的第一条语句
    • PowerShell (Core) 7+ 后台作业现在 - 幸运的是 - 使用与调用者相同的工作目录。

警告 类型保真度:

  • 由于值必须 跨进程边界 编组,因此必须涉及值的序列化和反序列化。后台作业使用与 PowerShell remoting, which - with the exception of a handful of well-known types, including .NET primitive types - results in loss of type fidelity, both on passing values to background jobs and receiving output from them - see
  • 相同的序列化基础结构

background 作业的首选替代方案:thread 作业,来自 Start-ThreadJob:

PowerShell (Core) 7+ 附带 ThreadJob 模块,它提供 Start-ThreadJob cmdlet ;在 Windows PowerShell 中,您可以 按需安装它 .

  • 此外,PowerShell (Core) 7+ 提供与 ForEach-Object cmdlet 扩展基本相同的功能,通过 -Parallel 参数,该参数在每个输入对象的单独线程中执行传递给它的脚本块。

Start-ThreadJob 与 PowerShell 的其他作业管理 cmdlet 完全集成,但使用 threads(即 in-process 并发)而不是 子进程 ,这意味着:

  • 执行速度更快
  • 使用更少的资源
  • 没有类型保真度损失(尽管您可以 运行 进入线程安全问题并且可能需要显式同步)

此外,调用者的工作目录是继承的。

$using: / -ArgumentList 的需求同样适用。

  • 对于 ForEach-Object -Parallel,正在考虑改进以允许在选择加入的基础上将调用者的状态复制到线程脚本块 - 请参阅 GitHub issue #12240

提供了 ForEach-Object -Parallel 的概述,并比较和对比了 Start-JobStart-ThreadJob