Start-Process
将是我在 PowerShell 中调用 PowerShell 的“最后手段选择”,特别是因为所有 I/O 都会变成字符串而不是(反序列化的)对象。
有两个备选方案:
1. 如果用户是本地管理员且已配置 PSRemoting
如果可以使用本地机器上的远程会话(不幸的是仅限于本地管理员),我肯定会选择 Invoke-Command
:
$strings = Invoke-Command -FilePath C:\...\script1.ps1 -ComputerName localhost -Credential $credential
$strings
将包含结果。
2. 如果用户不是目标系统上的管理员
您可以通过以下方式编写自己的“仅限本地Invoke-Command
”:
- 使用不同的登录名创建
PowerShellProcessInstance
- 在该进程中创建一个runspace
- 在该进程外的runspace中执行代码
我在下面组合了这样一个函数,有关步骤说明,请参见内联注释:
function Invoke-RunAs
{
[CmdletBinding()]
param(
[Alias('PSPath')]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string]
${FilePath},
[Parameter(Mandatory = $true)]
[pscredential]
[System.Management.Automation.CredentialAttribute()]
${Credential},
[Alias('Args')]
[Parameter(ValueFromRemainingArguments = $true)]
[System.Object[]]
${ArgumentList},
[Parameter(Position = 1)]
[System.Collections.IDictionary]
$NamedArguments
)
begin
{
Write-Verbose "Creating PowerShellProcessInstance and runspace"
$ProcessInstance = [System.Management.Automation.Runspaces.PowerShellProcessInstance]::new($PSVersionTable.PSVersion, $Credential, $null, $false)
$Runspace = [runspacefactory]::CreateOutOfProcessRunspace($null, $ProcessInstance)
$Runspace.Open()
Write-Verbose "Runspace state is $($Runspace.RunspaceStateInfo)"
}
process
{
foreach($path in $FilePath){
Write-Verbose "In process block, Path:'$path'"
try{
$powershell = [powershell]::Create([initialsessionstate]::CreateDefault2()).AddCommand((Resolve-Path $path).ProviderPath, $true)
if($PSBoundParameters.ContainsKey('NamedArguments')){
Write-Verbose "Adding named arguments to script"
$powershell = $powershell.AddParameters($NamedArguments)
}
if($PSBoundParameters.ContainsKey('ArgumentList')){
Write-Verbose "Adding unnamed arguments to script"
foreach($arg in $ArgumentList){
$powershell = $powershell.AddArgument($arg)
}
}
$powershell.Runspace = $Runspace
$powershell.Invoke()
if($powershell.HadErrors){
foreach($e in $powershell.Streams.Error){
Write-Error $e
}
}
}
finally{
if($powershell -is [IDisposable]){
$powershell.Dispose()
}
}
}
}
end
{
foreach($target in $ProcessInstance,$Runspace){
if($target -is [IDisposable]){
$target.Dispose()
}
}
}
}
然后像这样使用:
$output = Invoke-RunAs -FilePath C:\path\to\script1.ps1 -Credential $targetUser -NamedArguments @{ClientDevice = "ClientName"}