Как предотвратить выход хоста и вернуть код выхода? - PullRequest
2 голосов
/ 20 февраля 2020

Ответ Ансгара Вичерса хорошо работает при запуске нового процесса PowerShell. { ссылка } Это работает как для cmd.exe, так и powershell.exe.

C:>type .\exit1.ps1
function ExitWithCode($exitcode) {
  $host.SetShouldExit($exitcode)
  exit $exitcode
}
ExitWithCode 23

В интерактивной оболочке cmd.exe.

C:>powershell -NoProfile -Command .\exit1.ps1
C:>echo %ERRORLEVEL%
23
C:>powershell -NoProfile -File .\exit1.ps1
C:>echo %ERRORLEVEL%
23

В PowerShell интерактивная оболочка.

PS C:>powershell -NoProfile -Command .\exit1.ps1
PS C:>$LASTEXITCODE
23
PS C:>powershell -NoProfile -File .\exit1.ps1
PS C:>$LASTEXITCODE
23

ОДНАКО ... Запуск сценария .ps1 на существующем интерактивном хосте PowerShell полностью завершит работу хоста.

PS C:>.\exit1.ps1
    <<<poof! gone! outahere!>>>

Как можно предотвратить выход из хост-оболочка?

Ответы [ 2 ]

1 голос
/ 20 марта 2020

Не использовать $host.SetShouldExit(): он не вызывается кодом пользователя. Вместо этого он используется внутри PowerShell в ответ на оператор exit в коде пользователя .

Просто используйте exit 23 напрямую в вашем скрипте exit1.ps1, который будет делать то, что вы хотите:

  • При запуске в сеансе PowerShell скрипт установит код выхода 23 без выхода процесс PowerShell в целом; используйте $LASTEXITCODE для последующего запроса.

    .\exit.ps1; $LASTEXITCODE # -> 23
    
  • При запуске через PowerShell CLI :

    • с -File, код завершения, установленный сценарием, автоматически становится кодом выхода процесса PowerShell, который может проверить вызывающий; при вызове из cmd.exe, %ERRORLEVEL% отражает этот код выхода.

      powershell -File .\exit.ps1
      :: This outputs 23
      echo %ERRORLEVEL%
      
    • с -Command, дополнительная работа необходимо, поскольку PowerShell просто отображает любой ненулевой код выхода на 1, что приводит к потере указанного c кода выхода; чтобы компенсировать это, просто выполните exit $LASTEXITCODE в качестве последнего оператора :

      powershell -Command '.\exit.ps1; exit $LASTEXITCODE'
      :: This outputs 23
      echo %ERRORLEVEL%
      

Для больше информации о том, как PowerShell устанавливает коды выхода , см. Этот ответ .


Если :

  • , то вы этого не сделаете контролировать, как ваш скрипт вызывается через CLI, но должен гарантировать, что правильный код завершения будет сообщен, даже когда скрипт вызывается через -Command,

  • и вы готовы взять на себя риск использования $host.SetShouldExit(), даже если он не предназначен для прямого использования,

, вы можете попробовать следующее:

function ExitWithCode($exitcode) {
  if ([Environment]::CommandLine -match ( # Called via the CLI? (-File or -Command)
    ' .*?\b' + 
    [regex]::Escape([IO.Path]::GetFileNameWithoutExtension($PSCommandPath)) +
    '(?:\.ps1\b| |$)')
  ) {
    # CAVEAT: While this sets the exit code as desired even with -Command,
    #         the process terminates instantly.
    $host.SetShouldExit($exitcode)
  }
  else {
    # Exit normally, which in interactive session exits the script only.
    exit $exitcode
  }
}

ExitWithCode 23

Функция ищет имя файла исполняемого скрипта в командной строке процесса, чтобы определить, вызывается ли включающий скрипт напрямую через CLI , через автомат c $PSCommandPath переменная, которая содержит полный путь скрипта.

Если это так, то применяется вызов $host.SetShouldExit(), чтобы убедиться, что код выхода установить как задумано даже в случае вызова через -Command.
Обратите внимание, что это равносильно перепрофилированию эффективного внутреннего .SetShouldExit() метода.
Удивительно, эта перестановка работает, даже если после вызова сценария внутри строки -Command появляются дополнительные команды, но учтите, что это неизменно означает, что статус успешного выполнения действительно последней команды - если это не вызов сценария - будет эффективно игнорироваться.

Этот подход не является надежным [1] , но , вероятно, работает достаточно хорошо на практике.


[1]
* Может быть ложных срабатываний , учитывая, что ищется только файл name , без расширения (поскольку -Command позволяет опустить .ps1 расширение вызываемых скриптов).
* Может быть ложных негативов , если скрипт вызывается с помощью другого скрипта.

1 голос
/ 20 февраля 2020

Как я могу предотвратить выход из оболочки хоста?

Вы можете проверить, является ли запущенный в данный момент процесс PowerShell дочерним по отношению к другому родительскому процессу PowerShell, и вызвать только $host.SetShouldExit() когда это условие верно. Например:

function ExitWithCode($exitcode) {
   # Only exit this host process if it's a child of another PowerShell parent process...
   $parentPID = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$PID" | Select-Object -Property ParentProcessId).ParentProcessId
   $parentProcName = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$parentPID" | Select-Object -Property Name).Name
   if ('powershell.exe' -eq $parentProcName) { $host.SetShouldExit($exitcode) }

   exit $exitcode
}
ExitWithCode 23

Надеюсь, это поможет.

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