powershell 为什么"using“作用域可以在本地使用Start-Job,而不能使用Invoke-Command?

0yg35tkg  于 2023-01-13  发布在  Shell
关注(0)|答案(3)|浏览(160)

为什么PowerShell不允许在本地使用Invoke-Command时使用using作用域?根据文档,using修饰符只能用于远程命令。
从PowerShell 3.0开始,您可以使用Using作用域修饰符来标识远程命令中的局部变量。
在本地运行Invoke-Command时可以演示此行为:

$myServerName = 'www.google.com'
Invoke-Command { ping $using:myServerName }

这将引发以下错误:
无法检索Using变量。Using变量只能与脚本工作流中的Invoke-Command、Start-Job或InlineScript一起使用。当Using变量与Invoke-Command一起使用时,只有在远程计算机上调用脚本块时,Using变量才有效。
这个错误表明远程使用using修饰符只在远程使用Invoke-Command时有效。那么,如果我们尝试使用Start-Job运行同样的操作,会发生什么呢?

$myServerName = 'www.google.com'
$j = Start-Job { ping $using:myServerName }
while( $j.State -eq 'Running' ){ Start-Sleep -s 1 }
Receive-Job $j

它不会抛出错误,并且得到了预期的输出:

Pinging www.google.com [172.217.6.132] with 32 bytes of data:
 Reply from 172.217.6.132: bytes=32 time=20ms TTL=56
 Reply from 172.217.6.132: bytes=32 time=19ms TTL=56
 Reply from 172.217.6.132: bytes=32 time=19ms TTL=56
 Reply from 172.217.6.132: bytes=32 time=19ms TTL=56

为什么文档声明using作用域修饰符只能远程工作,而它显然也可以在本地上下文中使用?同样,如果它在本地Start-Job上下文中工作,是什么阻止它在本地Invoke-Command中工作?

bq3bfh9z

bq3bfh9z1#

当使用"using"时是这样的,因为using的定义规定,
从PowerShell 3.0开始,您可以使用Using作用域修饰符来标识远程命令中的局部变量
无论何时使用$using,都必须提供-ComputerName-Session参数,无论目标服务器是本地服务器还是远程服务器。
前。

$myServerName = 'www.google.com'
Invoke-Command { ping $using:myServerName }
### BIG ERROR.

$myServerName = 'www.google.com'
Invoke-Command { ping $using:myServerName } -computername $env:COMPUTERNAME
### Ping response.

$myServerName = 'www.google.com'
Invoke-Command { ping $myServerName }
### Ping Reponse.

$using:仅在少数特定上下文中受支持,这些上下文有一个共同点:正在当前运行空间之外运行的代码-所有其他上下文既不需要也不支持它。(@mklement0)
[Invoke-CommandStart-JobInlineScript是支持使用$using:在当前本地会话中传递变量的已知上下文。]
有关可以在何处使用$using的文档

hjzp0vay

hjzp0vay2#

注:此答案不包括PowerShell * 工作流 ,因为它们是PowerShell [Core] v6+ 中不再支持的 * 过时技术-请参阅this blog post

对于执行 out-of-runspace 的任何东西,即 * 不直接在调用者的runspace(线程)中执行,您需要$using:作用域以便嵌入来自调用者作用域的变量 * 值 *[1],以便out-of-runspace代码可以访问它。

  • 相反,所有 * 其他 * 上下文 * 既不需要也不支持 * $using:
  • 包括 * 本地 * Invoke-Command调用,例如您的调用(由于缺少-ComputerName-Session参数);然而,这种呼吁很少有必要(见下文)。
    超出运行空间上下文概述
    *远程执行的命令,从Invoke-Command-ComputerName参数开始。
  • Runspace-local 使用Invoke-Command-这是 * 不使用 * -ComputerName-Session时发生的情况-既不需要也不支持$using:引用(它在调用者作用域的 * 子作用域 * 中运行,或者,使用-NoNewScope时,直接在调用者的作用域中运行)。
  • 在运行空间本地使用Invoke-Command几乎没有必要,因为&,调用(执行)操作符(在子作用域中执行),和.,(点-)源操作符(直接在调用者的作用域中执行),是更简洁和有效的替代方法。
  • 请注意,如果您使用-ComputerName参数将 local 计算机作为目标,则该命令仍被视为远程执行,即它通过PowerShell的远程处理基础架构,并且适用与真正远程执行相同的规则。
    *后台作业,从Start-Job开始
    *线程作业,通过Start-ThreadJob启动。
  • 在PowerShell [Core] v7+中,这还包括传递到的脚本块

使用-Parallel交换机的ForEach-Object

远程执行的命令和后台作业 * 在进程外运行*[2],对于跨越这些进程边界的值,它们经历了基于XML的序列化和反序列化,这通常涉及类型保真度的损失-在输入和输出上都是如此。

  • 有关背景信息,请参见this answer
  • 请注意,这不仅适用于通过$using:嵌入的值,还适用于通过-ArgumentList-Args)参数作为 arguments 传递给Invoke-Command [-ComputerName]Start-Job的值。
    线程作业则相反,因为它们在同一进程 * 的不同运行空间(线程)* 中运行,接收$using:变量值作为其原始活动对象,并以类似方式返回此类对象。
    警告是,如果运行空间(线程)都访问给定的可变引用类型示例,则可能需要跨运行空间(线程)的显式同步*-这在ForEach-Object -Parallel中最有可能发生。
  • 不过,一般来说,**线程作业在大多数情况下都是后台作业的更好替代品,**因为它们的性能明显更好、资源使用更少、类型保真度更高。

[1]注意,这意味着运行空间外的代码 * 永远不能修改调用者作用域 * 中的变量。(但不在远程处理期间和后台作业中),如果变量 value 恰好是 reference type 的示例(例如集合类型),则有可能在另一线程中修改该示例,这需要跨线程同步修改,如果多个线程执行修改。
[2]与远程命令不同,后台作业在 * 同一台计算机 * 上运行,但在(隐藏的)PowerShell * 子进程 * 中运行。

xam8gpfp

xam8gpfp3#

如果不是远程,则不需要使用:

invoke-command { ping $myservername }

请注意,您必须是admin才能在localhost上调用。

相关问题