poke
poke

Reputation: 387557

Change 'standard error' to 'standard output'

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

Answers (2)

x0n
x0n

Reputation: 52410

Try:

$erroractionpreference = "silentlycontinue"
.\myscript.ps1

Upvotes: 1

Roman Kuzmin
Roman Kuzmin

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

Related Questions