IT专业人士很少只在本地计算机上工作。使用PowerShell的Invoke-Command cmdlet,我们不必这样做!该cmdlet允许我们无缝地编写代码,就好像我们在本地计算机上工作一样。
通过使用PowerShell远程功能,Invoke-Command
cmdlet是一个常用的PowerShell cmdlet,允许用户在PSSession中执行代码。这个PSSession可以是之前使用New-PSSession
cmdlet创建的,也可以快速创建和销毁一个临时会话。
将Invoke-Command视为PowerShell的psexec。尽管它们的实现方式不同,但概念是相同的。获取一小段代码或命令并在远程计算机上“本地”运行。
但要使Invoke-Command
正常工作,您必须在远程计算机上启用并可用PowerShell Remoting。默认情况下,所有Windows Server 2012 R2或更高版本的机器都已启用它,并具有适当的防火墙例外。如果不幸的是,您仍在使用Server 2008机器,有多种设置Remoting的方法,但一个简单的方法是在远程机器上运行winrm quickconfig
或Enable-PSRemoting
。
为了演示如何使用“Invoke-Command”执行“临时命令”,即不需要创建新的PSSession的命令,假设你有一个远程的Windows Server 2012 R2或更高版本的域加入计算机。当在工作组计算机上工作时,情况会变得有点混乱。我将打开我的PowerShell控制台,键入Invoke-Command
并按Enter。
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
为了证明脚本块内部的代码在远程计算机上执行,让我们运行hostname
命令。该命令将返回正在运行它的计算机的主机名。在我的本地计算机上运行hostname
将返回它的名称。
现在让我们将一个包含相同代码的脚本块传递给Invoke-Command
。不过,在这之前,我们忘记了一个必需的参数:ComputerName
。我们必须告诉Invoke-Command
在哪台远程计算机上运行此命令。
请注意,hostname
的输出现在是远程计算机WEBSRV1
的名称。你已经在WEBSRV1上运行了一些代码。在脚本块中运行简单的代码并传递给单个远程计算机是Invoke-Command
的最简单应用,但它可以做得更多。
将本地变量传递给远程脚本块
你的脚本内不会只有一个Invoke-Command引用。你的脚本可能会有数十行长,包含在不同地方定义的变量、在模块中定义的函数等等。即使只是在一对花括号中包裹一些代码看起来可能无害,实际上你正在改变代码运行的整个范围。毕竟,你正在将该代码发送到远程计算机。远程计算机对于你机器上的所有本地代码几乎一无所知,除非它在脚本块中。
例如,也许你有一个带有计算机名称和文件路径参数的函数。这个函数的目的是在远程计算机上运行一些软件安装程序。你能够传递计算机名称和已经位于远程计算机上的“本地”文件路径给安装程序。
下面的函数似乎是合理的,对吧?让我们运行它。
由于我使用了”与”运算符,它会因为一个晦涩的错误消息而失败。代码并没有错,但由于$InstallerFilePath
为空,它却失败了,即使你通过函数参数传递了一个值。我们可以通过用Write-Host
替换”与”来测试这一点。
新的函数行: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Installer path is: $InstallerFilePath" }
请注意,$InstallerFilePath
的值为空。该变量未展开,因为未传递到远程计算机。要将本地定义的变量传递到远程脚本块,我们有两个选项;我们可以在脚本块内使用$using:
前缀变量名,或者可以使用Invoke-Command
参数ArgumentList
。让我们看看这两者。
ArgumentList 参数
将本地变量传递到远程脚本块的一种方法是使用Invoke-Command
的ArgumentList
参数。该参数允许您将本地变量传递给参数,并在脚本块中用占位符替换本地变量引用。
将本地变量传递给ArgumentList
参数很容易。
有些人感到困惑的部分是如何在脚本块内部结构变量。我们不是使用{ & $InstallerPath }
,而是需要将其更改为{ & $args[0] }
或{param($foo) & $foo }
。两者都能正常工作,但应该选择哪个呢?
ArgumentList
参数是一个对象集合。对象集合允许您一次传递一个或多个对象。在这种情况下,我只传递一个。
当执行时,Invoke-Command 命令会获取该集合,然后将其注入脚本块,从本质上将其转换为一个名为 $args
的数组。记住 $args -eq ArgumentList
。在这一点上,您将像使用数组一样引用集合的每个索引。在我们的案例中,集合中只有一个元素($InstallerFilePath
),它“转换”为 $args[0]
,意思是该集合中的第一个索引。然而,如果您有更多元素,您可以用 $args[1]
、$args[2]
等来引用它们。
此外,如果您更愿意为脚本块变量分配更好的变量名称,您也可以像函数一样向脚本块添加参数。毕竟,脚本块 就是一个匿名函数。要创建脚本块参数,创建一个 param 块,带上参数的名称。创建后,然后在脚本块中引用该参数,就像下面这样。
在这种情况下,ArgumentList
集合中的元素被“映射”到按顺序定义的参数中。参数名称并不重要;重要的是顺序。 Invoke-Command
将获取 ArgumentList
集合中的第一个元素,查找第一个参数并映射这些值,然后对第二个、第三个等依次执行相同的操作。
使用 $Using 构造
用户: 使用$using
结构是将本地变量传递到远程脚本块的另一种流行方式。这种结构允许您重用现有的本地变量,但只需在变量名称前加上$using:
。无需担心$args
集合或添加参数块。
PowerShell $using
结构要简单得多,但如果你有机会学习Pester,你会发现ArgumentList
会是你的好朋友。
Invoke-Command和New-PSSession
从技术上讲,本文仅涉及Invoke-Command,但为了演示其用途,我们需要简要介绍一下New-PSSession
命令。回想一下,我之前提到过Invoke-Command
可以使用“临时”命令或使用现有会话。
在本文中,我们一直在远程计算机上运行“临时”命令。我们一直在启动一个新会话、运行代码并关闭它。这对于一次性的情况来说没问题,但在您对同一台计算机执行数十个命令时就不太合适了。在这种情况下,最好是预先创建一个新的PSSession,并通过New-PSSession
来重用现有的PSSession。
在运行任何命令之前,您首先需要使用New-PSSession
创建一个 PSSession。我们可以通过简单地运行$session = New-PSSession -ComputerName WEBSRV1
来实现这一点。这将在服务器上创建一个远程会话,并在我的本地计算机上创建对该会话的引用。在这一点上,我可以用Session
替换我的ComputerName
引用,并将Session
指向我保存的$session
变量。
当运行时,您会注意到性能更快,因为会话已经建立。然而,完成后,使用Remove-PSSession
移除打开的会话是很重要的。
总结
Invoke-Command PowerShell 命令是最常见和强大的命令之一。我个人几乎使用所有命令中的最多之一。它的易用性和在远程计算机上运行任何代码的能力非常强大,是我建议从头到尾学习的一个命令!