Change 'standard error' to 'standard output' - powershell

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.

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
}

Try:
$erroractionpreference = "silentlycontinue"
.\myscript.ps1

Related

Out-Host -Paging error "The method or operation is not implemented. " (ISE)

I execute this Powershell command:
Get-Process | Out-Host -Paging
But it returns me error:
ut-lineoutput : The method or operation is not implemented.
At line:1 char:1
+ Get-Process | Out-Host -Paging
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [out-lineoutput], NotImplementedException
+ FullyQualifiedErrorId : System.NotImplementedException,Microsoft.PowerShell.Commands.OutLineOutputCommand
I have checked help for Out-host and with paging it should be returnig results page by page.
Basically I want to see results page by page not everythign flush. Please help
The -Paging flag doesn't work for powershell_ise.exe.
Use powershell.exe
Another option though it may not be exactly what you need...
$file="c:\temp\out.txt"
dir -Recurse | Out-File $file
notepad $file
to paginate, you could do this...
$page = 20
$step = $page
$command = get-process
$count = $command.count
while ($count -gt ($page - $step)){
$command[($page - $step)..$page]
Pause
$page += $step + 1
}
Just set $page to whatever you want to see per page ... it's actually 1 more than you'd like because you start with 0 NOT 1. so 20 will show 21 per page.
some one turned this into a module, so you could do that...
http://community.idera.com/powershell/powertips/b/tips/posts/using-more-in-the-powershell-ise
his script basically reads each line individually, then if you hit your counter, it just spits out "press enter to continue" then reads the next line etc...

Powershell: how to remove stack trace when command returns an error?

When a command returns an error, I get like an error message, plus what looks like a full stack of the error:
C:\> dir foo
dir : Cannot find path 'C:\foo' because it does not exist.
At line:1 char:1
+ dir foo
+ ~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\foo:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
Is there a way to only see the error (that's the only thing usefull for me) and not display the full stack ?
Like:
C:\> dir foo
dir : Cannot find path 'C:\foo' because it does not exist.
You need to catch the error if you want to control how or what is displayed:
try {
dir foo -ErrorAction Stop
} catch {
Write-Host $_
}
Sometimes you'll need to add -ErrorAction Stop (or $ErrorActionPreference = 'Stop') to ensure that all errors are terminating (so they can be caught).
All powershell errors are captured in the auto variable $error. The item at index zero ($error[0]) is the most recent, index 1 next to most recent, etc. Each object in the array is a System.Management.Automation.ErrorRecord. An ErrorRecord contains a number of properties. You can use the select-object cmdlet to see a subset of properties:
$error[0]|select * -excludeproperty *stacktrace
If you want to be able to view the error record at an arbitrary while you're developing a script I'd suggest a function in your profile maybe like:
function show-myError {
param( $errorIndex = 0)
# You can also add anything else you want to exclude
$error[0]|select * -exclude *stacktrace
}
sal err show-myError
On the other hand if you're getting an error at a specific place in a specific script you can use catch/try as suggested in earlier answer. Or if you don't have a specific place, but do have a specific script, then I suggest trap, which you can place at the top of a function or top of a PS1 file. In the catch block or trap block you can output $error[0] using select with the -exclude parameter.

powershell CategoryInfo NotSpecified and newlines

I have an app that prints text to stderr, so to save to a file, I do this:
.\NGPQUERY.exe -spoof -stages -diag 2> c:\foo.txt
None of the text is special characters, other than crlf at the end of lines.
In DOS, the output is fine.
In powershell the output is almost fine.
The first line of text is this:
RTE Routings for BRI to LED - 00:
I get this error message at the top of the output:
NGPQUERY.exe : RTE Routings for BRI to LED - 00:
At line:1 char:15
+ .\ngpquery.exe <<<< -spoof -stages -diag 2> e:\foo
+ CategoryInfo : NotSpecified: (RTE Routings for BRI to LED - 00: :String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Then throughout the output, line feeds are added at seemingly random locations. So my question is how do I get rid of the error and the random line feeds.
Also powershell outputs file that is twice as large as the dos file, I'm guessing its unicode. So I would like to know the best way get an ansi output too.
If you only need STDERR, this should be enough:
$oPsi = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$oPsi.FileName = "NGPQUERY.exe"
$cArgs = #("-spoof", "-stages", "-diag")
$oPsi.Arguments = $cArgs
$oPsi.CreateNoWindow = $true
$oPsi.UseShellExecute = $false
$oPsi.RedirectStandardError = $true
$oProcess = New-Object -TypeName System.Diagnostics.Process
$oProcess.StartInfo = $oPsi
[Void]$oProcess.Start()
$sStdErr = $oProcess.StandardError.ReadToEnd()
[Void]$oProcess.WaitForExit()
$sStdErr | Out-File -Encoding "ASCII" -FilePath "C:\foo.txt"
This is an old question, but I got here because I had a similar problem and found the easiest solution to be to call cmd.exe and surround the command in quotes, to prevent Powershell from interpreting it. So in the above case that would be:
cmd.exe /c ".\NGPQUERY.exe -spoof -stages -diag 2> c:\foo.txt"

PowerShell: Manage errors with Invoke-Expression

I try to figure how to determine if a command throw with Invoke-Expression fail.
Even the variable $?, $LASTEXITCODE or the -ErrorVariable don't help me.
For example :
PS C:\> $cmd="cat c:\xxx.txt"
Call $cmd with Invoke-Expression
PS C:\> Invoke-Expression $cmd -ErrorVariable err
Get-Content : Cannot find path 'C:\xxx.txt' because it does not exist.
At line:1 char:4
+ cat <<<< c:\xxx.txt
+ CategoryInfo : ObjectNotFound: (C:\xxx.txt:String) [Get-Content], ItemNotFoundExcep
tion
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
The $? is True
PS C:\> $?
True
The $LASTEXITCODE is 0
PS C:\> $LASTEXITCODE
0
And the $err is empty
PS C:\> $err
PS C:\>
The only way I found is to redirect STD_ERR in a file and test if this file is empty
PS C:\> Invoke-Expression $cmd 2>err.txt
PS C:\> cat err.txt
Get-Content : Cannot find path 'C:\xxx.txt' because it does not exist.
At line:1 char:4
+ cat <<<< c:\xxx.txt
+ CategoryInfo : ObjectNotFound: (C:\xxx.txt:String) [Get-Content], ItemNotFoundExcep
tion
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Is it the only and best way to do this ?
I was going crazy trying to make capturing the STDERR stream to a variable work. I finally solved it. There is a quirk in the invoke-expression command that makes the whole 2&>1 redirect fail, but if you omit the 1 it does the right thing.
function runDOScmd($cmd, $cmdargs)
{
# record the current ErrorActionPreference
$ep_restore = $ErrorActionPreference
# set the ErrorActionPreference
$ErrorActionPreference="SilentlyContinue"
# initialize the output vars
$errout = $stdout = ""
# After hours of tweak and run I stumbled on this solution
$null = iex "& $cmd $cmdargs 2>''" -ErrorVariable errout -OutVariable stdout
<# these are two apostrophes after the >
From what I can tell, in order to catch the stderr stream you need to try to redirect it,
the -ErrorVariable param won't get anything unless you do. It seems that powershell
intercepts the redirected stream, but it must be redirected first.
#>
# restore the ErrorActionPreference
$ErrorActionPreference=$ep_restore
# I do this because I am only interested in the message portion
# $errout is actually a full ErrorRecord object
$errrpt = ""
if($errout)
{
$errrpt = $errout[0].Exception
}
# return a 3 member arraylist with the results.
$LASTEXITCODE, $stdout, $errrpt
}
It sounds like you're trying to capture the error output of a native in a variable without also capturing stdout. If capturing stdout was acceptable, you'd use 2>&1.
Redirecting to a file might be the simplest. Using Invoke-Expression for it's -ErrorVariable parameter almost seems like a good idea, but Invoke-Expression has many problems and I usually discourage it.
Another option will look a little cumbersome, but it can be factored into a function. The idea is to merge output streams using 2>&1, but then split them again based on the type of the object. It might look like this:
function Split-Streams
{
param([Parameter(ValueFromPipeline=$true)]$InputObject)
begin
{
$stdOut = #()
$stdErr = #()
}
process
{
if ($InputObject -is [System.Management.Automation.ErrorRecord])
{
# This works well with native commands but maybe not as well
# for other commands that might write non-strings
$stdErr += $InputObject.TargetObject
}
else
{
$stdOut += $InputObject
}
}
end
{
,$stdOut
,$stdErr
}
}
$o, $e = cat.exe c:\xxx.txt 2>&1 | Split-Streams

powershell: how to print the total call stacks when error happen?

suppose I have the following code, when the error happens, I'd like to see the error that the error first happened at function b, and then happened at function a. But in fact it only tells me the error happen at function a, since function a could be called many times, I don't know which outer function calling function a caused the problem
cls
function a{
Remove-Item "not-exist-item"
}
function b{
a
}
b
Remove-Item : Cannot find path 'C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\not-exis
t-item' because it does not exist.
At C:\Users\Daniel.Wu\AppData\Local\Temp\2\a.ps1:***3 char:14***
+ Remove-Item <<<< "not-exist-item"
+ CategoryInfo : ObjectNotFound: (C:\Program File...\not-exist-item:String) [Remove-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
If you are on PowerShell v2.0, use Get-PSCallStack. If you're still on v1, use a function like this:
function Get-CallStack {
trap { continue }
1..100 | foreach {
$var = Get-Variable -scope $_ MyInvocation
$var.Value.PositionMessage -replace "`n"
}
}
One option is to set
$ErrorActionPreference = 'inquire'
and then invoke the problematic script. On error you are prompted for choices, choose Suspend to enter into the nested prompt mode. Then type
Get-PSCallStack
or even
Get-PSCallStack | fl
to get the current call stack information.
Does get-help about_debuggers provide any illumination?