web-dev-qa-db-fra.com

Comment écrire sur l'erreur standard dans PowerShell?

J'ai du mal à comprendre comment à la fois faire écho au flux d'erreur standard et rediriger le flux d'erreur d'un exécutable.

Je viens d'un Bourne Shell et Korn Shell background, dont j'utiliserais;

# Write to stderr
echo "Error Message!" >&2

# Redirect stderr to file
/do/error 2>/tmp/err.msg
39
Brett Ryan

Utilisation Write-Error pour écrire à stderr. Pour rediriger stderr vers un fichier, utilisez:

 Write-Error "oops" 2> /temp/err.msg

ou

 exe_that_writes_to_stderr.exe bogus_arg 2> /temp/err.msg

Notez que PowerShell écrit les erreurs sous forme d'enregistrements d'erreur. Si vous voulez éviter la sortie détaillée des enregistrements d'erreur, vous pouvez écrire les informations d'erreur vous-même comme ceci:

PS> Write-Error "oops" -ev ev 2>$null
PS> $ev[0].exception
oops

-EV est court (un alias) pour -ErrorVariable. Toutes les erreurs seront stockées dans la variable nommée par l'argument de ce paramètre. PowerShell signalera toujours l'erreur à la console, sauf si nous redirigeons l'erreur vers $null.

42
Keith Hill

Vous voulez probablement ceci:

$Host.ui.WriteErrorLine('I work in any PowerShell Host')

Vous pouvez également voir ce qui suit, mais cela suppose que votre hôte PowerShell est une fenêtre/périphérique de console, donc je considère que c'est moins utile:

[Console]::Error.WriteLine('I will not work in the PowerShell ISE GUI')
31
Chris Sears

Remarque:

  • Cette réponse concerne l'écriture sur stderr du point de vue du monde extérieur lorsqu'un script PowerShell est appelé à partir de là; tandis que la réponse est écrite du point de vue du shell Windows, cmd.exe, elle s'applique également aux shell Unix tels que bash lorsqu'il est combiné avec PowerShell Core.

  • En revanche, from within Powershell , vous devez utiliser Write-Error, Comme expliqué dans Réponse de Keith Hill .

  • Malheureusement, il n'y a pas unifié approche qui fonctionnera des deux à l'intérieur PowerShell et de l'extérieur - voir cette réponse à moi pour une discussion.


Pour ajouter à @ la grande réponse de Chris Sear :

Alors que $Host.ui.WriteErrorLine Devrait fonctionner sur tous les hôtes, il n'écrit pas (par défaut) sur stderr lorsqu'il est appelé via cmd.exe, comme à partir d'un fichier de commandes. [Console]::Error.WriteLine, En revanche, toujours fait .

Donc si vous voulez écrire un script PowerShell qui joue bien en termes de flux de sortie lorsqu'il est invoqué depuis cmd.exe, utilisez la fonction suivante, Write-StdErr, Qui utilise [Console]::Error.WriteLine Dans la fenêtre de console standard de l'hôte PS/cmd.exe (), et $Host.ui.WriteErrorLine sinon:

<#
.SYNOPSIS
Writes text to stderr when running in a regular console window,
to the Host''s error stream otherwise.

.DESCRIPTION
Writing to true stderr allows you to write a well-behaved CLI
as a PS script that can be invoked from a batch file, for instance.

Note that PS by default sends ALL its streams to *stdout* when invoked from
cmd.exe.

This function acts similarly to Write-Host in that it simply calls
.ToString() on its input; to get the default output format, invoke
it via a pipeline and precede with Out-String.

#>
function Write-StdErr {
  param ([PSObject] $InputObject)
  $outFunc = if ($Host.Name -eq 'ConsoleHost') {
    [Console]::Error.WriteLine
  } else {
    $Host.ui.WriteErrorLine
  }
  if ($InputObject) {
    [void] $outFunc.Invoke($InputObject.ToString())
  } else {
    [string[]] $lines = @()
    $Input | % { $lines += $_.ToString() }
    [void] $outFunc.Invoke($lines -join "`r`n")
  }
}

Informations générales facultatives:

En interne, PowerShell a plus que les flux de sortie traditionnels (stdout et stderr), et leur nombre a augmenté au fil du temps (essayez Write-Warning "I'll go unheard." 3> $null Comme exemple, et lisez la suite sur - Get-Help about_Redirection ; notez que la page liée ne reflète pas encore le flux 6 Pour Write-Information , introduit dans PowerShell v5).

Lors de l'interface avec le monde extérieur, PowerShell doit mapper les flux de sortie non traditionnels vers stdout et stderr.

Étrangement, cependant, PowerShell envoie par défaut tous ses flux (y compris Write-Host Et $Host.ui.WriteErrorLine() output) vers stdout lorsqu'il est appelé à partir de cmd.exe, même si le mappage du flux d'erreurs de PowerShell à stderr serait le choix logique. Ce comportement est en vigueur depuis (au moins) la v2 et s'applique toujours à partir de la v5.1 (et ne changera probablement pas pour des raisons de compatibilité descendante).

Vous pouvez le vérifier avec la commande suivante, si vous l'appelez depuis cmd.exe:

powershell -noprofile -command "'out'; Write-Error 'err'; Write-Warning 'warn'; Write-Verbose -Verbose 'verbose'; $DebugPreference='Continue'; write-debug 'debug'; $InformationPreference='Continue'; Write-Information 'info'; Write-Host 'Host'; $Host.ui.WriteErrorLine('uierr'); [Console]::Error.WriteLine('cerr')" >NUL

La commande écrit dans tous les flux de sortie PowerShell (lorsque vous exécutez sur une version antérieure à PowerShell-v5, vous verrez un message d'erreur supplémentaire concernant Write-Information, Qui a été introduit dans PowerShell v5) et contient cmd.exe Redirige stdout uniquement vers NUL (c'est-à-dire supprime sortie stdout; >NUL).

Vous verrez non sortie sauf cerr (de [Console]::Error.WriteLine(), qui écrit directement sur stderr) - tous les flux de PowerShell ont été envoyés à stdout.

Peut-être encore plus étrangement, il est possible de capturer le flux d'erreur de PowerShell, mais uniquement avec une redirection:

Si vous remplacez >NUL Par 2>NUL Ci-dessus, c'est exclusivement la sortie flux d'erreur et $Host.ui.WriteErrorLine() de PowerShell qui sera supprimée; bien sûr, comme pour toute redirection, vous pouvez également l'envoyer dans un fichier -. (Comme indiqué, [Console]::Error.WriteLine()] toujours renvoie à stderr, que ce dernier soit redirigé ou non.)

Pour donner un exemple plus ciblé (encore une fois, exécutez à partir de cmd.exe):

powershell -noprofile -command "'out'; Write-Error 'err'" 2>NUL

La sortie ci-dessus ne produit que la sortie de out - Write-Error Est supprimée.

Pour résumer :

  • Sans aucune redirection (cmd.exe) Ou avec seulement a stdout redirection (>... Ou 1>...) , PowerShell envoie tous ses flux de sortie à stdout.

  • Avec une redirection stderr (2>...), PowerShell sélectivement envoie son flux d'erreur à stderr (indépendamment du fait que stdout soit également redirigé ou non).

  • En corollaire, l'idiome commun suivant ne fonctionne pas pas comme prévu:
    powershell ... >data-output.txt Ceci, comme on pourrait s'y attendre, n'enverra que stdout dans le fichier data-output.txt Lors de l'impression stderr sortie vers le terminal; à la place, vous devez utiliser
    powershell ... >data-output.txt 2>err-output.tmp; type err-output.tmp >&2; del err-output.tmp

Il s'ensuit que PowerShell est au courant des redirections de cmd.exe Et ajuste son comportement intentionnellement. (Cela est également évident lorsque PowerShell produit coloré sortie dans la cmd.exe console tout en supprimant les codes de couleur lorsque la sortie est redirigée vers un fichier. )

29
mklement0