使用 Start-Process 使用参数运行脚本块命令

dev*_*xer 7 powershell

为什么powershell认为$dirnull在设置位置而不是在写入输出时?

$command = {
    param($dir)
    Set-Location $dir
    Write-Output $dir
}

# run the command as administrator
Start-Process powershell -Verb RunAs -ArgumentList "-NoExit -Command $command 'C:\inetpub\wwwroot'"
Run Code Online (Sandbox Code Playgroud)

这导致以下输出:

Set-Location : Cannot process argument because the value of argument "path" is null. Change the value of argument
"path" to a non-null value.
At line:3 char:2
+  Set-Location $dir
+  ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Set-Location], PSArgumentNullException
    + FullyQualifiedErrorId : ArgumentNull,Microsoft.PowerShell.Commands.SetLocationCommand

C:\inetpub\wwwroot
Run Code Online (Sandbox Code Playgroud)

我也试过:

$command = {
    param($dir)
    Set-Location $dir
    Write-Output $dir
}

$outerCommand = {
    Invoke-Command -ScriptBlock $command -ArgumentList 'C:\inetpub\wwwroot'
}

# run the command as administrator
Start-Process powershell -Verb RunAs -ArgumentList "-NoExit -Command $outerCommand"
Run Code Online (Sandbox Code Playgroud)

但后来我得到了:

Invoke-Command : Cannot validate argument on parameter 'ScriptBlock'. The argument is null. Provide a valid value for
the argument, and then try running the command again.
At line:2 char:30
+  Invoke-Command -ScriptBlock $command 'C:\inetpub\wwwroot'
+                              ~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Invoke-Command], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.InvokeCommandCommand
Run Code Online (Sandbox Code Playgroud)

可能的线索:如果我设置一个局部变量而不是使用参数,它会完美地工作:

$command = {
    $dir = 'C:\inetpub\wwwroot'
    Set-Location $dir
    Write-Output $dir
}

# run the command as administrator
Start-Process powershell -Verb RunAs -ArgumentList "-NoExit -Command $command"
Run Code Online (Sandbox Code Playgroud)

类似的 Q/As 并没有完全回答我的问题:

mkl*_*nt0 7

$command是一个脚本块( { ... }) 并且将脚本块字符串化会导致其文字内容,不包括封闭的{}

因此,您的可扩展字符串"-NoExit -Command $command 'C:\inetpub\wwwroot'"从字面上扩展为以下字符串 - 请注意{ ... }原始脚本块周围的缺失:

 -NoExit -Command 
    param($dir)
    Set-Location $dir
    Write-Output $dir
 'C:\inetpub\wwwroot'
Run Code Online (Sandbox Code Playgroud)

由于丢失了封闭的{and },由powershell产生的新进程Start-Process悄悄地忽略了孤立param($dir)语句,并且,鉴于新进程因此没有$dir变量(假设它也不是自动变量),命令失败,因为Set-Location $dir等同于到Set-Location $null,失败。[1]

请注意,您永远不能像这样将脚本块传递给Start-Process所有参数必须是字符串,因为只有字符串才能传递给外部进程


在您的情况下,最简单的解决方案是:

  • $command您的可扩展字符串中的参考附上{ ... }以补偿由于字符串化而造成的此外壳的损失

  • 预先&确保在新进程中调用生成的脚本块。

这是一个有效的解决方案:

Start-Process powershell -Verb RunAs -ArgumentList `
  "-NoExit -Command & { $command } 'C:\inetpub\wwwroot'"
Run Code Online (Sandbox Code Playgroud)

重要提示:虽然手头的特定脚本块不需要,但具有嵌入"字符的"..."任何脚本块不会被目标进程正确解析为整个字符串的一部分;为了防止这种情况,它们必须被转义为\",这是外部程序所期望的 - 包括通过其 CLI 的 PowerShell:

# Script block with embedded " chars.
$command = {
    param($dir)
    Set-Location $dir
    "Current dir: $dir"
}

# Embed the script block with " escaped as \"
Start-Process powershell -Verb RunAs -ArgumentList `
  "-NoExit -Command & { $($command -replace '"', '\"') } 'C:\inetpub\wwwroot'"
Run Code Online (Sandbox Code Playgroud)

[1] 在Windows PowerShell 中失败;在 PowerShell Core 中,相当于Set-Location 不带参数,更改为当前用户的主文件夹。