是否能够测试是否安装了PowerShell v6(可以),如果是,则为某些CmdLets调用该shell。这将在PowerShell v5.1中运行的脚本中调用。我不能完全转移到v6,因为还有其他依赖项在这种环境下还没有工作,但是,v6对某些CmdLets提供了重要的优化,导致操作改进了200多倍(具体来说,Invoke-WebRequest
该调用将导致下载大文件 - 在v5.1中,4GB文件下载需要1个多小时,在v6中,使用相同子网上的相同机器需要大约30秒。
但是,我还构建了一组动态参数,用于扩展到CmdLets参数列表。例如,构建的参数列表如下所示:
$SplatParms = @{
Method = "Get"
Uri = $resource
Credential = $Creds
Body = (ConvertTo-Json $data)
ContentType = "application/json"
}
正常运行CmdLet会按预期工作:
Invoke-RestMethod @SplatParms
在过去的几天里,我查看了本论坛和其他地方的各种帖子。我们可以创建一个简单的脚本块,可以调用,也可以按预期工作:
$ConsoleCommand = { Invoke-RestMethod @SplatParms }
& $ConsoleCommand
但是,尝试在Start-Process
CmdLet中传递相同的内容失败了,因为我猜测参数哈希表没有被评估:
Start-Process pwsh -ArgumentList "-NoExit","-Command &{$ConsoleCommand}" -wait
结果:
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 }
我想我不得不以某种方式传递参数作为参数,以便可以对它们进行评估和splatted,但是,语法使我望而却步。我甚至不确定Start-Process
是否是最好使用的CmdLet,而是我应该查看其他内容,例如Invoke-Command
,还是完全不同的东西?
将这个CmdLet的结果返回到原始shell会很棒,但此刻,它只需要一些功能。
答案 0 :(得分:3)
注意:原则上,此答案中的技术不仅可以应用于从Windows PowerShell到PowerShell Core的调用,还可以应用于相反的方向,以及实例之间在Windows和Unix上的相同的 PowerShell版本。
您不需要Start-Process
;您可以使用脚本块直接调用pwsh
:
pwsh -c { $SplatParms = $Args[0]; Invoke-RestMethod @SplatParms } -args $SplatParms
请注意需要将哈希表作为参数 传递,而不是作为脚本块的一部分传递。
[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.
...
此操作失败,因为$PSVersionTable.PSVersion
在PowerShell Core中的类型为[System.Management.Automation.SemanticVersion]
,这是Windows PowerShell自v5.1起不可用的类型(在Windows PowerShell中,相同属性的类型为{{ 1}})。
[System.Version]
实例的解决方法:[PSCredential]
使用脚本块从PowerShell中调用另一个PowerShell实例涉及CLIXML格式的对象的序列化和反序列化,也在PowerShell远程处理中使用。
通常,有许多.NET类型反序列化无法忠实地重新创建,并且在这种情况下创建{em}模拟原始类型实例的pwsh -c {
$SplatParms = $Args[0];
$SplatParams.Credential = [pscredential] $SplatParams.Credential;
Invoke-RestMethod @SplatParms
} -args $SplatParms
个实例,其中(通常是隐藏的){{ 1}}反映前缀为[PSCustomObject]
从Windows PowerShell 5.1 / PowerShell Core 6.0.0开始,.pstypenames
(Deserialized.
)的实例也会出现这种情况,这会阻止他们在目标会话中使用直接 - 见this GitHub issue。
幸运的是,只需将反序列化的对象强制转换回[pscredential]
似乎可以正常工作。
答案 1 :(得分:0)
尝试在5.1会话中针对6.0创建New-PSSession。
安装powershell core 6.0并运行Enable-PSRemoting
后,为6.0创建了一个新的PSSessionConfiguration:
PS > Get-PSSessionConfiguration
Name : microsoft.powershell
PSVersion : 5.1
StartupScript :
RunAsUser :
Permission : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed
Name : microsoft.powershell.workflow
PSVersion : 5.1
StartupScript :
RunAsUser :
Permission : BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed
Name : microsoft.powershell32
PSVersion : 5.1
StartupScript :
RunAsUser :
Permission : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed
Name : PowerShell.v6.0.0
PSVersion : 6.0
StartupScript :
RunAsUser :
Permission : NT AUTHORITY\INTERACTIVE AccessAllowed, BUILTIN\Administrators AccessAllowed, BUILTIN\Remote Management Users AccessAllowed
在您的父脚本中,使用6.0配置名称PowerShell.v6.0.0
创建新会话,并将其传递给您需要的任何后续Invoke-Command
。结果作为对象返回。根据mklement0的回答,Scriptblocks可能需要通过-ArgumentList
传递的局部变量。
$ps6sess = New-PSSession -ComputerName localhost -ConfigurationName 'PowerShell.v6.0.0'
$results = Invoke-Command -Session $ps60sess -ScriptBlock {Param($splatthis) Invoke-WebRequest @splatthis} -ArgumentList $SplatParms
知道会话在Invoke-Command调用之间持续存在也可能很有用。例如,您创建的任何新变量都可以在该会话中的后续调用中访问:
PS > Invoke-Command -Session $ps60sess -ScriptBlock {$something = 'zers'}
PS > Invoke-Command -Session $ps60sess -ScriptBlock {write-host $something }
zers
PSCredential传递的麻烦似乎不是这种方法的问题:
$ps6sess = New-PSSession -ComputerName localhost -ConfigurationName 'PowerShell.v6.0.0'
$credential = Get-Credential -UserName 'TestUser'
$IRestArgs = @{
Method='GET'
URI = 'https://httpbin.org'
Credential = $credential
}
$IRestBlock = {Param($splatval) Invoke-RestMethod @splatval}
Invoke-Command -Session $ps6sess -ScriptBlock $IRestBlock -ArgumentList $IRestArgs
# no error
pwsh -c {
Param ($SplatParms)
#$SplatParams.Credential = [pscredential] $SplatParams.Credential;
Invoke-RestMethod @SplatParms
} -args $IRestArgs
# error - pwsh : cannot process argument transformation on
# parameter 'Credential. username
也许在ps6会话中知道它正在从ps5.1接收一个块并知道如何容纳。
答案 2 :(得分:-1)
使用启动过程立即闪现是不合适的,不得不对此进行研究,无论如何,我的反应是构建一系列参数,而不是使用它们进行尝试。