Выполнение команды script-block с параметром, используя Start-Process - PullRequest
0 голосов
/ 05 марта 2019

Почему PowerShell думает, что $dir равно null при установке местоположения, но не при записи вывода?

$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'"

Это приводит к следующему выводу:

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

Я также пытался:

$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"

Но потом я получил:

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

Возможная подсказка: если я устанавливаю локальную переменную вместо использования параметра, это работаетотлично:

$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"

Похожий вопрос / Поскольку это не совсем ответило на мой вопрос:

1 Ответ

0 голосов
/ 05 марта 2019

$command - это блок скрипта ({ ... }) и , преобразующий в строку блок скрипта, в результате его литерал содержимое ,исключая включающие { и }.

Следовательно, ваша расширяемая строка "-NoExit -Command $command 'C:\inetpub\wwwroot'" буквально расширяется до следующей строки - обратите внимание на отсутствующий { ... } вокруг исходного блока скрипта:

 -NoExit -Command 
    param($dir)
    Set-Location $dir
    Write-Output $dir
 'C:\inetpub\wwwroot'

Из-за потери включающих { и } новый процесс powershell, порожденный Start-Process, тихо игнорировал потерянный оператор param($dir) и, учитывая, что новый процесс поэтому не имел $dirпеременная (учитывая, что она не является автоматической переменной), команда завершилась неудачно, поскольку Set-Location $dir равносильно Set-Location $null, что не удалось. [1]

Обратите внимание, что вы никогда не сможете передать блоки скриптов как таковые в Start-Process - все аргументы должны быть строк , потому что только строки могут быть переданы внешние процессы .


Самое простое решение в вашем случае:

  • заключают ссылку $command в расширяемую строку в { ... }, чтобы компенсировать потерю этого корпуса из-за строкового кодирования

  • и prepend & для обеспечения вызова результирующего блока скрипта в новом процессе.

Вот рабочийрешение:

Start-Process powershell -Verb RunAs -ArgumentList `
  "-NoExit -Command & { $command } 'C:\inetpub\wwwroot'"

Важное замечание: Хотя это и не требуется для конкретного блока сценария под рукой, любой блок сценария, имеющий встроенных символов ".не будет правильно обработан как часть общей строки "..." целевым процессом ;чтобы предотвратить это, они должны быть экранированы как \", чего ожидают внешние программы, включая PowerShell через CLI:

# 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'"

[1]Сбой в Windows PowerShell ;в PowerShell Core это равносильно Set-Location без аргументов , которое заменяет домашнюю папку текущего пользователя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...