Swi*_*ter 3 powershell powershell-v6.0
是为了能够测试以查看是否安装了 PowerShell v6(可以),如果是,则为某些 CmdLet 调用该 shell。这将在 PowerShell v5.1 中运行的脚本中调用。我无法完全转移到 v6,因为还有其他依赖项在此环境中尚不可用,但是,v6 对某些 CmdLet 进行了重大优化,从而将操作改进了 200 多倍(具体而言,Invoke-WebRequest调用将导致下载一个大文件 - 在 v5.1 中,一个 4GB 的文件需要 1 小时以上才能下载,在 v6 中,使用同一子网上的相同机器,这将需要大约 30 秒。
但是,我还建立了一组动态参数,用于插入 CmdLets 参数列表。例如,构建的参数列表将类似于:
$SplatParms = @{
Method = "Get"
Uri = $resource
Credential = $Creds
Body = (ConvertTo-Json $data)
ContentType = "application/json"
}
Run Code Online (Sandbox Code Playgroud)
并且运行 CmdLet 通常会按预期工作:
Invoke-RestMethod @SplatParms
Run Code Online (Sandbox Code Playgroud)
在过去的几天里,我查看了这个论坛和其他地方的各种帖子。我们可以创建一个简单的脚本块,它可以被调用并按预期工作:
$ConsoleCommand = { Invoke-RestMethod @SplatParms }
& $ConsoleCommand
Run Code Online (Sandbox Code Playgroud)
但是,尝试在Start-ProcessCmdLet 中传递相同的东西失败了,因为我猜参数哈希表没有被评估:
Start-Process pwsh -ArgumentList "-NoExit","-Command &{$ConsoleCommand}" -wait
Run Code Online (Sandbox Code Playgroud)
结果是:
Invoke-RestMethod : Cannot validate argument on parameter 'Uri'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:1 char:22
+ &{ Invoke-RestMethod @SplatParms }
Run Code Online (Sandbox Code Playgroud)
我想我必须以某种方式将参数作为参数传递,以便可以对它们进行评估和处理,但是,语法使我无法理解。我什至不确定是否Start-Process是最好的 CmdLet,但我应该看看其他东西,比如Invoke-Command,还是完全不同的东西?
将这个 CmdLet 的结果返回到原始 shell 会很棒,但目前,它只会采取一些功能。
注意:原则上,此答案中的技术不仅可以应用于从 Windows PowerShell 到 PowerShell Core 的调用,还可以应用于相反的方向,以及在 Windows 和 Unix 上相同PowerShell 版本的实例之间。
你不需要Start-Process; 您可以使用脚本块直接调用pwsh:
pwsh -c { $SplatParms = $Args[0]; Invoke-RestMethod @SplatParms } -args $SplatParms
Run Code Online (Sandbox Code Playgroud)
请注意需要将哈希表作为参数传递,而不是作为脚本块的一部分。
[PSCredential]这种方式传递实例存在问题- 请参见底部的解决方法。这将同步执行,甚至在控制台中打印脚本块的输出。
该警告是,捕捉这样的输出-要么将其分配给变量或重定向到一个文件-如果可以返回所不具备的呼叫会话类型的实例失败。
作为一个次优的解决方法,你可以使用-o Text(-OutputFormat Text)的感谢,PetSerAl来捕获输出为文本,正是因为它会打印到控制台
(运行pwsh -h看所有选项)。
输出默认以序列化的 CLIXML 格式返回,调用 PowerShell 会话将其反序列化回对象。如果无法识别序列化对象的类型,则会发生错误。
一个简单的例子(从Windows PowerShell执行):
# This FAILS, but you can add `-o text` to capture the output as text.
WinPS> $output = pwsh -c { $PSVersionTable.PSVersion } # !! FAILS
pwsh : Cannot process the XML from the 'Output' stream of 'C:\Program Files\PowerShell\6.0.0\pwsh.exe':
SemanticVersion XML tag is not recognized. Line 1, position 82.
...
Run Code Online (Sandbox Code Playgroud)
这将失败,因为PowerShell Core 中$PSVersionTable.PSVersion的类型[System.Management.Automation.SemanticVersion]是 PowerShell Core,这是自 v5.1 起在 Windows PowerShell 中不可用的类型(在 Windows PowerShell 中,相同属性的类型为[System.Version])。
[PSCredential]实例的解决方法:pwsh -c {
$SplatParms = $Args[0];
$SplatParams.Credential = [pscredential] $SplatParams.Credential;
Invoke-RestMethod @SplatParms
} -args $SplatParms
Run Code Online (Sandbox Code Playgroud)
使用脚本块从 PowerShell 中调用另一个 PowerShell 实例涉及 CLIXML 格式的对象的序列化和反序列化,也用于 PowerShell 远程处理。
一般来说,有许多.NET类型的反序列化不能忠实地再现,在这种情况下,创建[PSCustomObject]该实例模拟原始类型的实例,用(通常隐藏).pstypenames属性反映前缀原来的类型名称Deserialized.
从 Windows PowerShell 5.1 / PowerShell Core 6.0.0 开始,[pscredential]( [System.Management.Automation.PSCredential]) 的实例也会发生这种情况,这会阻止它们在目标会话中直接使用 - 请参阅此 GitHub 问题。
然而,幸运的是,简单地将反序列化的对象转换回[pscredential]似乎可以工作。