Question:
I have a PowerShell script that writes to the error output. The script can be as simple as the following:
1 2 3 4 |
Write-Error 'foo' Start-Sleep -s 5 Write-Error 'bar' |
The script I actually call spawns an external process that takes a while to process and writes to standard error.
Now when I call the script like this:
1 2 |
. myScript.ps1 |
I get the error message with PowerShell’s usual behaviour (i.e. red text and lots of debugging information). As that text has no relation to PowerShell in my actual application, I don’t need those debugging information and it only makes the result less readable (and impossible to process).
Is there a way to redirect that output directly into standard output, so that I just get the text?
I tried something like this:
1 2 |
Write-Host ( . myScript.ps1 2>&1 ) |
But that delays the output until everything is completed.
About the “debugging information”, when I run the script right now, the output looks like this (in red):
1 2 3 4 5 6 7 8 9 10 11 12 |
C:\path\to\myScript.ps1 : foo Bei Zeile:1 Zeichen:2 + . <<<< 'C:\path\to\myScript.ps1' + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,myScript.ps1 C:\path\to\myScript.ps1 : bar Bei Zeile:1 Zeichen:2 + . <<<< 'C:\path\to\myScript.ps1' + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,myScript.ps1 |
When I run the script with Write-Host ( . myScript.ps1 2>&1 )
, where the error output is written to the standard output, I get a result like this:
1 2 |
foo bar |
That is exactly what I would like the output to be, except that the Write-Host (..)
makes the output only appear after the script has terminated, so I cannot receive any information on the progress of said script.
To come actually closer to my actual problem (because it’s hard to explain that with pure PowerShell); I’ve got the following Python script that resembles approximately what the command line program I use does, i.e. it does some processing and prints out the progress to the standard error:
1 2 3 4 5 6 7 8 |
#!/usr/bin/python import sys, time sys.stderr.write( 'Progressing... ' ) sys.stderr.flush() time.sleep( 5 ) sys.stderr.write( 'done.\n' ) sys.stderr.flush() |
Now, when I call it with python script.py
, it works correctly and prints out the “progressing” line, waits 5 seconds and then prints the “done” to PowerShell (as normal text). The problem is now that I want to run this in a job, like this: Start-Job { python script.py }
.
The job gets started correctly, and also works fine in the background, but when I want to check its progress via Receive-Job <id>
, I get no output at all at first, and after the script/program finished (i.e. after the 5 seconds), I get the following output (in red again):
1 2 3 4 |
Progressing... done. + CategoryInfo : NotSpecified: (Progressing... done.:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError |
Obviously that is not what I am trying to get. Instead I want to get the actual output that was printed to the standard error, both live (i.e. when it happens in the script) and without that PowerShell related debugging text.
Answer:
According to the edit section of the question, this should be suitable:
1 2 |
. MyScript.ps1 2>&1 | %{ Write-Host $_ } |
It writes just “foo” and “bar” and they appear as soon as they happen.
EDIT
Actually this is even simpler and works fine, too:
1 2 |
. MyScript.ps1 2>&1 | Write-Host |
But I keep the original answer. That code allows to process the output ($_
) dynamically and do something else (i.e. not just write it to the host).
EDIT 2
External program that writes to STDERR:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { for (int i = 0; i < 100; ++i) { Console.Error.WriteLine("Step " + i); System.Threading.Thread.Sleep(2000); } } } } |
The script that starts this application as a job and receives its output periodically.
1 2 3 4 5 6 7 8 |
$ErrorActionPreference = 'continue' $job = Start-Job { C:\TEMP\Test\ConsoleApplication1.exe 2>&1 } for(;;) { Receive-Job $job | Write-Host Start-Sleep 2 } |
The script does exactly what you need (for your edit 2 section): it shows the output as soon as it is available and it does not show unwanted extra error information.
P.S. This version works, too:
1 2 3 4 5 6 |
$job = Start-Job { C:\TEMP\Test\ConsoleApplication1.exe } for(;;) { Receive-Job $job 2>&1 | Write-Host Start-Sleep 2 } |