当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] Invoke-Command:运行远程代码的最佳方式

作者:精品下载站 日期:2024-12-14 12:37:08 浏览:15 分类:玩电脑

Invoke-Command:运行远程代码的最佳方式


IT 专业人员很少只在本地计算机上工作。使用 PowerShell Invoke-Command cmdlet,我们不必这样做!这个 cmdlet 允许我们无缝地编写代码,就像我们在本地计算机上工作一样。

通过使用 PowerShell 远程处理功能,Invoke-Command cmdlet 是一种常用的 PowerShell cmdlet,允许用户在 PSSession 内执行代码。此 PSSession 可以是之前使用 New-PSSession cmdlet 创建的,也可以快速创建和拆除临时会话。

相关:PowerShell 远程处理:终极指南

将 Invoke-Command 视为 PowerShell psexec。尽管它们的实现方式不同,但概念是相同的。获取一些代码或命令并在远程计算机上“本地”运行它。

为了使 Invoke-Command 正常工作,您必须在远程计算机上启用并可用 PowerShell 远程处理。默认情况下,所有 Windows Server 2012 R2 或更高版本的计算机都会启用它以及相应的防火墙例外。如果您不幸仍然拥有 Server 2008 计算机,可以通过多种方法来设置远程处理,但一种简单的方法是在服务器上运行 winrm QuickconfigEnable-PSRemoting远程机器。

为了演示 Invoke-Command 如何与“临时命令”(即不需要创建新的 PSSession 的命令)配合使用,假设您有一台远程 Windows Server 2012 R2 或更高版本的加入域的计算机。在工作组计算机上工作时,事情会变得有点混乱。我将打开 PowerShell 控制台,输入 Invoke-Command 并按 Enter 键。

PS> Invoke-Command
cmdlet Invoke-Command at command pipeline position 1
Supply values for the following parameters:
ScriptBlock:

我立即被要求提供一个脚本块。脚本块是我们要在远程计算机上运行的代码。

为了证明脚本块内的代码是在远程计算机上执行的,让我们运行 hostname 命令。此命令将返回正在运行的计算机的主机名。在我的本地计算机上运行 hostname 会产生,它的名称。

PS> hostname
MACWINVM

现在,让我们将脚本块中包含相同代码的脚本块传递给 Invoke-Command。但在此之前,我们忘记了一个必需的参数:ComputerName。我们必须告诉 Invoke-Command 在哪台远程计算机上运行此命令。

PS> Invoke-Command -ScriptBlock { hostname } -ComputerName WEBSRV1 WEBSRV1

请注意,hostname 的输出现在是远程计算机 WEBSRV1 的名称。您已在WEBSRV1上运行了一些代码。在脚本块内运行简单代码并传递到单个远程计算机是 Invoke-Command 最简单的应用程序,但它可以做更多的事情。

将本地变量传递到远程脚本块

脚本中不会有单个 Invoke-Command 引用。您的脚本可能有几十行长,有定义的变量、在模块中定义的函数等等。尽管只是将一些代码括在几个大括号中可能看起来很无辜,但实际上您正在更改代码运行的整个范围。毕竟,您正在将该代码发送到远程计算机。除了脚本块中的内容之外,该远程计算机不知道您计算机上的所有本地代码。

例如,也许您有一个带有计算机名称和文件路径参数的函数。此功能的目的是在远程计算机上运行某些软件安装程序。您可以将计算机名称和“本地”文件路径传递给已位于远程计算机上的安装程序。

下面的函数看起来很合理,对吧?让我们运行它。

function Install-Stuff {
    param(
    	[Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName,
        
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$InstallerFilePath
	)
    Invoke-Command -ComputerName $ComputerName -ScriptBlock { & $InstallerFilePath }
}

PS> Install-Stuff -ComputerName websrv1 -InstallerFilePath 'C:\install.exe'
The expression after '&' in a pipeline element produced an object that was not valid. It must result in a command name, a script block, or a CommandInfo object.
+ CategoryInfo          : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : BadExpression
+ PSComputerName        : websrv1

由于我使用了 & 运算符,它失败并出现模糊的错误消息。代码没有错误,但失败了,因为即使您通过函数参数传递了一个值,$InstallerFilePath 仍为空。我们可以通过用 Write-Host 替换 & 符号来测试这一点。

新功能行: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Installer path is: $InstallerFilePath" }

PS> Install-Stuff -ComputerName websrv1 -InstallerFilePath 'C:\install.exe'
Installer path is:
PS>

请注意,$InstallerFilePath 的值什么都没有。该变量尚未扩展,因为它没有传递到远程计算机。要将本地定义的变量传递到远程脚本块,我们有两个选择:我们可以在脚本块内使用 $using: 作为变量名的前缀,也可以使用 Invoke-Command 参数 ArgumentList。让我们看看两者。

参数列表参数

将局部变量传递到远程脚本块的一种方法是使用 Invoke-Command ArgumentList 参数。此参数允许您将局部变量传递给参数,并用占位符替换脚本块中的局部变量引用。

将局部变量传递给 ArgumentList 参数很容易。

Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { & $InstallerFilePath } -ArgumentList $InstallerFilePath

让一些人困惑的部分是如何构造脚本块内的变量。我们需要将其更改为 { & $args[0] }{param($foo) & 而不是使用 { & $InstallerPath } $foo}。两种方法的效果都是一样的,但是您应该使用哪一种呢?

ArgumentList 参数是一个对象集合。对象集合允许您一次传递一个或多个对象。在这种情况下,我只是传递一个。

执行时,Invoke-Command cmdlet 获取该集合,然后将其注入到脚本块中,实质上将其转换为名为 $args 的数组。请记住$args -eq ArgumentList。此时,您将像引用数组一样引用集合的每个索引。在我们的例子中,集合中只有一个元素 ($InstallerFilePath),它“翻译”为 $args[0],表示该集合中的第一个索引。但是,如果您有更多,您可以引用它们 $args[1]$args[2] 等等。

此外,如果您希望为脚本块变量分配更好的变量名称,您也可以像函数一样向脚本块添加参数。毕竟,脚本块只是一个匿名函数。要创建脚本块参数,请使用参数名称创建一个 param 块。创建后,然后在脚本块中引用该参数,如下所示。

Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { param($foo) & $foo } -ArgumentList $InstallerFilePath

在本例中,ArgumentList 集合中的元素按顺序“映射”到定义的参数。参数名称并不重要;重要的是顺序。 Invoke-Command 将获取 ArgumentList 集合中的第一个元素,查找第一个参数并映射这些值,对第二个、第三个等执行相同的操作。

$Using 结构

$using 构造是将局部变量传递到远程脚本块的另一种流行方法。此构造允许您重用现有的局部变量,但只需在变量名称前面添加 $using: 即可。无需担心 $args 集合,也无需添加参数块。

Invoke-Command -ComputerName WEBSRV1 -ScriptBlock { & $using:InstallerFilePath }

PowerShell $using 构造要简单得多,但如果您开始学习 Pester,您会发现 ArgumentList 将成为您的朋友。

调用命令和新 PSSession

从技术上讲,这篇文章仅涉及 Invoke-Command,但为了演示它的实用性,我们还需要简要介绍一下 New-PSSession 命令。回想一下之前我提到的 Invoke-Command 可以使用“临时”命令或使用现有会话。

在这篇文章中,我们只是在远程计算机上运行“临时”命令。我们一直在启动一个新会话,运行代码并将其拆除。这对于一次性情况来说很好,但对于在同一台计算机上执行数十个命令的情况来说就不太适用了。在这种情况下,最好通过提前使用 New-PSSession 创建一个来重用现有的 PSSession。

在运行任何命令之前,您首先需要使用 New-PSSession 创建一个 PSSession。我们只需运行 $session=New-PSSession -ComputerName WEBSRV1 即可完成此操作。这将在服务器上创建一个远程会话,并在我的本地计算机上创建对该会话的引用。此时,我可以用 Session 替换我的 ComputerName 引用,并将 Session 指向我保存的 $session 变量。

Invoke-Command -Session $session -ScriptBlock { & $using:InstallerFilePath }

运行时,您会发现性能更快,因为会话已经构建。不过,完成后,使用 Remove-PSSession 删除打开的会话非常重要。

概括

Invoke-Command PowerShell cmdlet 是最常见、最强大的 cmdlet 之一。这是我个人在几乎所有这些中使用最多的一个。它的易用性和在远程计算机上运行任何代码的能力非常强大,是我建议从头到尾学习的一个命令!

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯