Quando devo usar Write-Error vs. Throw? Erros de finalização vs. erros de finalização

145

Observando um script Get-WebFile no PoshCode, http://poshcode.org/3226 , notei esta engenhoca estranha para mim:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

Qual o motivo disso, ao contrário do seguinte?

$URL_Format_Error = [string]"..."
Throw $URL_Format_Error

Ou melhor ainda:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error

Pelo que entendi, você deve usar o Write-Error para erros que não terminam e o Throw para terminar os erros. Parece-me que você não deve usar o Write-Error seguido de Return. Existe alguma diferença?

Bill Barry
fonte
4
O que você quer dizer? Se Write_error permitir que o script continue, é muito compreensível ter uma declaração de retorno após Write-Error. O erro foi gravado e você retorna ao código que chamou a função em primeiro lugar. Desde Throw é para erros de terminação ela será cancelada automaticamente para que uma instrução de retorno em uma declaração lance é inútil
Gisli
1
@Gisli: É importante notar que returnse não retornar para o chamador no processbloco de uma função (avançado); em vez disso, prossegue para o próximo objeto de entrada no pipeline. De fato, este é o cenário típico para gerar erros sem finalização: se o processamento de outros objetos de entrada ainda for possível.
usar o seguinte comando
1
Observe que Throwgera um erro de terminação de script , que não é o mesmo que os erros de terminação de instrução acionados, por exemplo, por Get-Item -NoSuchParameterou 1 / 0.
precisa saber é o seguinte

Respostas:

188

Write-Errordeve ser usado se você deseja informar o usuário sobre um erro não crítico. Por padrão, tudo o que faz é imprimir uma mensagem de erro em texto em vermelho no console. Ele não impede que um pipeline ou um loop continue. Throwpor outro lado, produz o que é chamado de erro final. Se você usar o throw, o pipeline e / ou o loop de corrente serão encerrados. De fato, toda a execução será encerrada, a menos que você use uma trapou uma try/catchestrutura para lidar com o erro de finalização.

Há uma coisa a nota, se você definir $ErrorActionPreferencea"Stop" e usar Write-Errorele irá produzir um erro de encerramento .

No script ao qual você vinculou, encontramos o seguinte:

if ($url.Contains("http")) {
       $request = [System.Net.HttpWebRequest]::Create($url)
}
else {
       $URL_Format_Error = [string]"Connection protocol not specified. Recommended action: Try again using protocol (for example 'http://" + $url + "') instead. Function aborting..."
       Write-Error $URL_Format_Error
    return
   }

Parece que o autor dessa função queria interromper a execução dessa função e exibir uma mensagem de erro na tela, mas não queria que o script inteiro parasse de executar. O autor do script poderia ter usado, throwno entanto, isso significaria que você precisaria usar a try/catchao chamar a função.

returnsairá do escopo atual, que pode ser uma função, script ou bloco de scripts. Isso é melhor ilustrado com o código:

# A foreach loop.
foreach ( $i in  (1..10) ) { Write-Host $i ; if ($i -eq 5) { return } }

# A for loop.
for ($i = 1; $i -le 10; $i++) { Write-Host $i ; if ($i -eq 5) { return } }

Saída para ambos:

1
2
3
4
5

Uma pegadinha aqui está usando returncom ForEach-Object. Não interromperá o processamento como se poderia esperar.

Mais Informações:

Andy Arismendi
fonte
Ok, então o Throw irá parar tudo, o Write-Error + return irá parar apenas a função atual.
Bill Barry
@BillBarry Atualizei minha resposta um pouco com uma explicação de return.
Andy Arismendi
E o Erro de Gravação seguido pela saída (1) para garantir que o código de erro apropriado seja retornado ao SO? Isso é apropriado?
Pabrams
19

A principal diferença entre o cmdlet Write-Error e a palavra-chave throw no PowerShell é que o primeiro simplesmente imprime algum texto no fluxo de erro padrão (stderr) , enquanto o último termina o processamento do comando ou função em execução, que é então tratado pelo PowerShell enviando informações sobre o erro ao console.

Você pode observar o comportamento diferente dos dois nos exemplos que você forneceu:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

Neste exemplo, a returnpalavra-chave foi adicionada para interromper explicitamente a execução do script depois que a mensagem de erro foi enviada ao console. No segundo exemplo, por outro lado, a returnpalavra-chave não é necessária, pois a terminação é implicitamente feita por throw:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error
Enrico Campidoglio
fonte
2
se você tiver $ ErrorActionPreference = "Stop", o Write-Error também encerrará o processo.
Michael Freidgeim
4
Boas informações, mas enquanto o fluxo de erros do PowerShell é análogo ao fluxo stderr baseado em texto em outros shells, como todos os fluxos do PowerShell, ele contém objetos , como [System.Management.Automation.ErrorRecord]instâncias, que são coletadas por padrão na $Errorcoleção automática ( $Error[0]contém o erro mais recente). Mesmo se você apenas usar Write-Errorcom uma string , essa string será quebrada em uma [System.Management.Automation.ErrorRecord]instância.
mklement0
16

Importante : Existem 2 tipos de erros de finalização , que infelizmente os tópicos atuais da ajuda conflitam :

  • erros de terminação de instrução , conforme relatado por cmdlets em determinadas situações não recuperáveis ​​e por expressões nas quais ocorre uma exceção .NET / um erro de tempo de execução do PS; somente a instrução é finalizada e a execução do script continua por padrão .

  • roteiro -terminating erros (mais precisamente: runspace-terminação ), como quer desencadeadas porThrowou escalando um dos outros tipos de erro através de erro de ação valor de preferência variável / parâmetroStop.
    A menos que capturados, eles terminam o espaço de execução atual (encadeamento); isto é, eles finalizam não apenas o script atual, mas também todos os chamadores, se aplicável).

Para uma visão abrangente do tratamento de erros do PowerShell, consulte este problema de documentação do GitHub .

O restante desta postagem se concentra em erros que não terminam ou que encerram com instruções .


Para complementar as respostas úteis existentes, com foco no cerne da questão: Como você escolhe se deseja relatar um erro que encerra ou não encerra uma declaração ?

O Relatório de Erros do Cmdlet contém diretrizes úteis; deixe-me tentar um resumo pragmático :

A idéia geral por trás dos erros sem terminação é permitir o processamento "tolerante a falhas" de grandes conjuntos de entradas : a falha ao processar um subconjunto dos objetos de entrada não deve (por padrão) abortar o processo - potencialmente demorado - como um todo , permitindo inspecionar os erros e reprocessar apenas os objetos com falha posteriormente - conforme relatado por meio dos registros de erros coletados na variável automática $Error.

  • Relate um erro NÃO TERMINANDO , se seu cmdlet / função avançada:

    • aceita vários objetos de entrada , via entrada de pipeline e / ou parâmetros com valor de matriz, E
    • ocorrem erros para objetos de entrada ESPECÍFICOS , E
    • esses erros NÃO IMPEDEM O PROCESSAMENTO DE OUTROS objetos de entrada EM PRINCÍPIO ( situacionalmente , pode não haver objetos de entrada restantes e / ou objetos de entrada anteriores já podem ter sido processados ​​com êxito).
      • Em funções avançadas, use $PSCmdlet.WriteError()para relatar um erro sem fim ( Write-Errorinfelizmente, não faz com $?que seja definido $Falseno escopo do chamador - consulte este problema do GitHub ).
      • Manipulando um erro sem finalização: $?informa se o comando mais recente relatou pelo menos um erro sem finalização.
        • Portanto, $?ser $Falsepode significar que qualquer subconjunto (não vazio) de objetos de entrada não foi processado adequadamente, possivelmente o conjunto inteiro.
        • A variável de preferência $ErrorActionPreferencee / ou o parâmetro de cmdlet comum -ErrorActionpodem modificar o comportamento de erros sem finalização (apenas) em termos de comportamento de saída de erro e se os erros sem finalização devem ser escalados para os que terminam com script .
  • Relate um erro de TERMO DE DECLARAÇÃO em todos os outros casos .

    • Notavelmente, se ocorrer um erro em um cmdlet / função avançada que aceite apenas um objeto de entrada SINGLE ou NO e emita um objeto de saída NO ou SINGLE ou utilize apenas a entrada de parâmetro e os valores de parâmetro fornecidos impedirão uma operação significativa.
      • Nas funções avançadas, você deve usar $PSCmdlet.ThrowTerminatingError()para gerar um erro de encerramento de instrução.
      • Observe que, por outro lado, a Throwpalavra - chave gera um erro de terminação de script que interrompe o script inteiro (tecnicamente: o encadeamento atual ).
      • Manipulando um Erro de Finalização de Instrução: Um try/catchmanipulador ou trapinstrução pode ser usado (que não pode ser usado com erros de finalização ), mas observe que mesmo os erros de finalização de instrução , por padrão, não impedem a execução do restante do script. Assim como ocorre nos erros sem encerramento , $?reflete $Falsese a instrução anterior acionou um erro de encerramento.

Infelizmente, nem todos os cmdlets principais do PowerShell seguem essas regras :

  • Embora seja improvável que falhe, New-TemporaryFile(PSv5 +) reportaria um erro sem fim, se falhasse, apesar de não aceitar a entrada do pipeline e produzir apenas um objeto de saída - isso foi corrigido pelo menos no PowerShell [Core] 7.0, no entanto: consulte este GitHub questão .

  • Resume-JobA ajuda de alega que a aprovação de um tipo de trabalho não suportado (como um trabalho criado com o Start-Jobqual não há suporte, porque Resume-Jobse aplica apenas a trabalhos de fluxo de trabalho) causa um erro de encerramento, mas isso não ocorre no PSv5.1.

mklement0
fonte
8

Write-Errorpermite que o consumidor da função suprima a mensagem de erro com -ErrorAction SilentlyContinue(alternativamente -ea 0). Embora throwexija umatry{...} catch {..}

Para usar uma tentativa ... pegue com Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}
Rynant
fonte
Por que você precisa chamar Erro de gravação, se deseja suprimir a mensagem de erro?
Michael Freidgeim
8

Além da resposta de Andy Arismendi :

Se o Erro de gravação encerra ou não o processo, depende da $ErrorActionPreferenceconfiguração.

Para scripts não triviais, $ErrorActionPreference = "Stop"é uma configuração recomendada para falhar rapidamente.

"O comportamento padrão do PowerShell com relação a erros, que é continuar com erro ... parece muito VB6" On Error Resume Next ”-ish"

(em http://codebetter.com/jameskovacs/2010/02/25/the-exec-problem/ )

No entanto, faz Write-Error chamadas terminando.

Para usar o Erro de gravação como um comando sem finalização, independentemente de outras configurações do ambiente, você pode usar o parâmetro comum -ErrorAction com valor Continue:

 Write-Error "Error Message" -ErrorAction:Continue
Michael Freidgeim
fonte
0

Se sua leitura do código estiver correta, você estará correto. Erros de terminação devem ser utilizados throwe, se você estiver lidando com tipos .NET, também será útil seguir as convenções de exceção do .NET.

yzorg
fonte