Reputation: 387557
I have a PowerShell script that writes to the error output. The script can be as simple as the following:
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:
. 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:
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):
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:
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:
#!/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):
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.
Upvotes: 5
Views: 2647
Reputation: 42035
According to the edit section of the question, this should be suitable:
. 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:
. 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:
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.
$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:
$job = Start-Job { C:\TEMP\Test\ConsoleApplication1.exe }
for(;;) {
Receive-Job $job 2>&1 | Write-Host
Start-Sleep 2
}
Upvotes: 4