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.
Understanding the concept of redirections and file descriptors is very important when working on the command line. To redirect stderr and stdout , use the 2>&1 or &> constructs.
The regular output is sent to Standard Out (STDOUT) and the error messages are sent to Standard Error (STDERR). When you redirect console output using the > symbol, you are only redirecting STDOUT. In order to redirect STDERR, you have to specify 2> for the redirection symbol.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With