Завершение скрипта в PowerShell

394

Я искал способ завершить сценарий PowerShell (PS1), когда в функции возникает неисправимая ошибка. Например:

function foo() {
    # Do stuff that causes an error
    $host.Exit()
}

Конечно, нет такой вещи как $host.Exit(). Есть $host.SetShouldExit(), но это на самом деле закрывает окно консоли, а это не то, что я хочу. То, что мне нужно, это что-то эквивалентное Python, sys.exit()которое просто остановит выполнение текущего скрипта без дополнительной информации.

Редактировать: Да, это просто exit. Duh.

kprobst
источник
8
Если вы хотите избежать закрытия окна PowerShell или ISE в моем случае; используйте вместо этого «возврат». Это просто заканчивает текущий запущенный контекст. «Новый парень» подробно объясняет все варианты в образовательных целях; Возможно, вы захотите изменить свой принятый ответ (в настоящее время у него больше голосов «за») для будущих пользователей StackOverflow. Это также позволит вам отладить скрипт.
ZaxLofful
Отвечает ли это на ваш вопрос? Что именно означает «выход» в PowerShell?
Марк Шультхайс

Ответы:

389

Вы должны использовать в exitключевое слово .

Майкл Брей
источник
15
Черт возьми, если бы я не был так захвачен попыткой понять, как делать эти вещи умно, я бы, наверное, попробовал это для начала и понял, что это работает :) Спасибо.
kprobst
118
Как это принятый ответ? В частности, он «закрывает окно консоли», которое, как сказал аскер, «это не то, что я хочу».
claudekennilol
3
@claudekennilol Только тот, кто задает вопрос, может принять ответ. Либо они пропустили тот факт, что выход должен закрыть окно, либо они передумали и сочли это приемлемым для своих целей.
Изи
3
Он не закрывает окно консоли в v3 или v4, которые я использую. Что ж, так и будет, если вы запустите скрипт из проводника, но независимо от того, как вы закончите скрипт, он это сделает. При запуске из командного окна PowerShell оно не закрывается.
Джошуа Нурчик
14
Ответ «Нового парня» гораздо тщательнее, и дезертиры помечаются как принятые.
Джим Ахо
585

Я понимаю, что это старая запись, но я часто возвращаюсь к этой теме, так как это один из лучших результатов поиска при поиске по этой теме. Однако я всегда оставляю более смущенным, чем когда приходил из-за противоречивой информации. В конечном счете, я всегда должен выполнять свои собственные тесты, чтобы понять это. Так что на этот раз я опубликую свои выводы.

TL; DR Большинство людей захотят использовать Exitдля завершения запущенных скриптов. Однако, если ваш скрипт просто объявляет функции, которые впоследствии будут использоваться в оболочке, вы захотите использовать их Returnв определениях указанных функций.

Выход против возврата против перерыва

  • Выход: это «выйдет» из текущего запущенного контекста. Если вы вызываете эту команду из скрипта, он выйдет из скрипта. Если вы вызовете эту команду из оболочки, она выйдет из оболочки.

    Если функция вызывает команду Exit, она выходит из любого контекста, в котором она выполняется. Поэтому, если эта функция вызывается только из запущенного сценария, она завершает этот сценарий. Однако, если ваш скрипт просто объявляет функцию, чтобы ее можно было использовать из текущей оболочки, и вы запускаете эту функцию из оболочки, он выйдет из оболочки, потому что оболочка - это контекст, в котором выполняется функция, составляющая Exitкоманду.

    Примечание. По умолчанию, если щелкнуть правой кнопкой мыши сценарий, чтобы запустить его в PowerShell, после его запуска PowerShell автоматически закроется. Это не имеет ничего общего с Exitкомандой или чем-то еще в вашем скрипте. Это просто поведение PowerShell по умолчанию для сценариев, запускаемых с использованием этого конкретного метода запуска сценария. То же самое верно для командных файлов и окна командной строки.

  • Возврат: возврат к предыдущему пункту вызова. Если вы вызовете эту команду из скрипта (вне каких-либо функций), она вернется в оболочку. Если вы вызовете эту команду из оболочки, она вернется в оболочку (которая является предыдущей точкой вызова для одной команды, запущенной из оболочки). Если вы вызываете эту команду из функции, она вернется туда, откуда была вызвана функция.

    Выполнение любых команд после точки вызова, в которую она возвращается, будет продолжено с этой точки. Если скрипт вызывается из оболочки и содержит Returnкоманду вне каких-либо функций, то когда он возвращается в оболочку, больше нет команд для выполнения, что делает Returnиспользование таким образом по существу таким же, как Exit.

  • Перерыв: это вырвется из петель и переключит дела. Если вы вызываете эту команду, не находясь в цикле или переключении, она выйдет из сценария. Если вы вызываете Breakвнутри цикла, который вложен в цикл, он будет прерываться только из цикла, в котором он был вызван.

    Существует также интересная особенность, заключающаяся в том, что Breakвы можете добавить префикс цикла к метке, а затем выйти из этого помеченного цикла, даже если Breakкоманда вызывается внутри нескольких вложенных групп в этом помеченном цикле.

    While ($true) {
        # Code here will run
    
        :myLabel While ($true) {
            # Code here will run
    
            While ($true) {
                # Code here will run
    
                While ($true) {
                    # Code here will run
                    Break myLabel
                    # Code here will not run
                }
    
                # Code here will not run
            }
    
            # Code here will not run
        }
    
        # Code here will run
    }
Новый парень
источник
39
Также стоит отметить, что Exitможет принимать код возврата в качестве параметра (по умолчанию 0) - например Exit 3.
aucuparia
2
Я согласен, это гораздо лучший ответ. «Перерыв» может иметь непредвиденные последствия.
iagomartinez
Я не уверен, что ваш комментарий о «Выходе» является полностью правильным, хотя в целом это отличный ответ. Выход, кажется, заставляет ISE закрываться способом, которым, кажется, больше ничего не делает. Простой выход из контекста (например, завершение скрипта) не делает этого.
Билл К
1
@BillK Действительно. Я обновил ответ. Когда я впервые написал это, я помню, что не нашел никакой официальной документации по выходу. Я тогда сделал Get-Command Exitи Get-Alias Exitбез результатов. Затем я посмотрел, было ли это ключевое слово, и не нашел официальной документации по нему. Однако, глядя на это сейчас, я не знаю, как я пришел к такому выводу. Понятно, что это ключевое слово ( technet.microsoft.com/en-us/library/hh847744.aspx ). Возможно, потому что Exit - это единственное ключевое слово, у которого нет собственной темы about_ help, и поэтому в списке тем на левой боковой панели его нет.
Новый парень
7
Что насчет Броска?
JDC
80

Exitвыйдет из PowerShell тоже. Если вы хотите «вырваться» из текущей функции или скрипта - используйте Break:)

If ($Breakout -eq $true)
{
     Write-Host "Break Out!"
     Break
}
ElseIf ($Breakout -eq $false)
{
     Write-Host "No Breakout for you!"
}
Else
{
    Write-Host "Breakout wasn't defined..."
}
EverydayNerd
источник
5
Пожалуйста, не используйте, breakкогда хотите просто выйти из текущей функции ... вызов скриптов также может быть нарушен!
DannyMeister
49

Write-Error - для ошибок, не заканчивающихся, а throw - для ошибок завершения.

Командлет Write-Error объявляет нескончаемую ошибку. По умолчанию ошибки передаются в потоке ошибок в ведущую программу для отображения вместе с выводом.

Не прекращающиеся ошибки записывают ошибку в поток ошибок, но не останавливают обработку команд. Если для одного элемента в коллекции входных элементов объявляется нескончаемая ошибка, команда продолжает обрабатывать другие элементы в коллекции.

Чтобы объявить завершающую ошибку, используйте ключевое слово Throw. Для получения дополнительной информации см. About_Throw ( http://go.microsoft.com/fwlink/?LinkID=145153 ).

Грег Брей
источник
4
Это кажется наиболее правильным способом завершить действие с ошибкой. Он делегирует вызывающей стороне попытку обработать ошибку, что гораздо лучше, чем просто попытка внезапного завершения.
Пол Тернер
Для меня, по крайней мере, в функциях модуля, бросить выходы, но не установить код выхода. Это было выполнено через CLI, например powershell -command "& module-function ...". Мне нужно было преобразовать эти функции для преобразования в try-catch обертки и выхода из этой обертки-обертки, чтобы фактически вывести код завершения ошибки.
Джош
2
Не забывайте $PSCmdlet.ThrowTerminatingError()о тех случаях, когда бросок просто не может выполнить работу (известная проблема с отсутствием завершающих ошибок от throw)
Джарид
33

Завершает этот процесс и дает базовой операционной системе указанный код выхода.

https://msdn.microsoft.com/en-us/library/system.environment.exit%28v=vs.110%29.aspx

[Environment]::Exit(1)

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

gabriwinter
источник
3
В PowerShell вы можете просто использовать встроенный выход для этого, например Exit 1.
Джонас
3
встроенный выход не всегда будет работать так, как вы ожидаете. Попробуйте это в powershell: «Выход 5»> test1.ps1 powershell.exe. \ Test1.ps1 $ lastexitcode "[Environment] :: Exit (5)"> test2.ps1 powershell.exe. \ Test2.ps1 $ lastexitcode
gabriwinter
1
[Environment]::Exit(1)имеет побочный эффект выхода из моего окна powershell, когда я вызываю его в скрипте, а использование exit, похоже, не делает этого.
Джош Десмонд
25

Я думаю, что вы ищете Returnвместо Break. Разрыв обычно используется для циклов и разрывается только от самого внутреннего блока кода. Используйте Return для выхода из функции или скрипта.

обкрадывать
источник
7
Это не правильно - OP специально запрашивает выход из скрипта из функции . Returnпросто вернется из функции, а не из сценария. ( Returnна верхнем уровне сценария будет прекращен сценарий, но это не был вопрос.)
Майкл Соренс
1
был ли этот «ответ» на самом деле комментирует ответ EverydayNerd?
tkokasih
22

Создание исключения будет хорошо, особенно если вы хотите выяснить причину ошибки:

throw "Error Message"

Это сгенерирует завершающую ошибку.

Амр Бахаа
источник
4
Я не думаю, что это добавляет больше, чем ответ Грега Брея почти годом ранее stackoverflow.com/a/20556550/695671
Джейсон С.
12

Может быть, лучше использовать «ловушку». Ловушка PowerShell указывает кодовый блок, который запускается при завершении или ошибке. Тип

Get-Help about_trap

чтобы узнать больше об утверждении ловушки.

fpschultze
источник
10

По совпадению я обнаружил, что (например, просто , когда метка не существует ), кажется, выходит из всего скрипта (даже изнутри функции) и поддерживает работу хоста. Таким образом, вы можете создать функцию, которая разрывает скрипт из любого места (например, рекурсивный цикл), не зная текущей области (и создавая метки):Break <UnknownLabel>Break ScriptScript

Function Quit($Text) {
    Write-Host "Quiting because: " $Text
    Break Script
} 
утюг
источник
1
Ваш ответ верный, но имейте в виду, что это может вызвать проблемы у тех, кто вызывает ваш сценарий, если они не согласны с вашим желанием выйти из всего сценария: stackoverflow.com/questions/45746588/…
DannyMeister
5

Я использовал это для перезапуска программы. Я не знаю, поможет ли это, но это простой оператор if, требующий только двух разных записей. Это работает в PowerShell для меня.

$rerun = Read-Host "Rerun report (y/n)?"

if($rerun -eq "y") { Show-MemoryReport }
if($rerun -eq "n") { Exit }

Не знаю, поможет ли это, но я верю, что после завершения работы программы будет происходить прекращение работы программы. Однако в этом случае каждый определенный вход требует перечисленных и категоризированных выходных данных. Вы также можете сделать так, чтобы выход вызывал новую строку приглашения и таким образом завершал программу.

Майкл Мели
источник