Edit: I created a PowerShell UserVoice "suggestion" for (against?) this behavior; feel free to upvote.
PowerShell (5.1.16299.98, Windows 10 Pro 10.0.16299) is inserting newlines into my stderr when I redirect to file—as if to format for the console. Let's generate error messages of arbitrary length:
class Program
{
static void Main(string[] args)
{
System.Console.Error.WriteLine(new string('x', int.Parse(args[0])));
}
}
I compiled the above to longerr.exe
. Then I call it like this:
$ .\longerr.exe 60 2>error.txt
I ran the following script in a PowerShell console with window width 60:
$h = '.\longerr.exe : '.Length
$w = 60 - 1
$f = 'error.txt'
Remove-Item $f -ea Ignore
(($w-$h), ($w-$h+1), ($w), ($w+1), ($w*2-$h), ($w*2-$h+1)) |
% {
$_ >> $f
.\longerr.exe $_ 2>>$f
}
Now in a wider console I ran the following:
$ Get-Content $f | Select-String '^(?![+\t]|At line| )'
(I could have just opened the file in a text editor and trimmed lines.) Here's the output:
43
.\longerr.exe : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
44
.\longerr.exe :
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
59
.\longerr.exe :
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
60
.\longerr.exe : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxx
102
.\longerr.exe : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
103
.\longerr.exe : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x
Why is PowerShell doing this? Can I make it stop? I'd rather not have to do something like this:
.\longerr.exe $_ 2>&1 |
% {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
$_.Exception.Message | Out-File -FilePath $f -Append
}
}
First, all the opening and closing of that file can be slow (I know I can add more code and use a StreamWriter
), and second, there is still a problem (bug?) with that approach, which I won't go into in this question.
For a sanity check, I ran longerr.exe 1000 2>test.txt
in cmd.exe
; it inserted no spurious linebreaks.
Thanks to TessellatingHeckler's comment pointing me to the question Why does PowerShell chops message on stderr?, I've been able to fix both problems in my question—the main one and the one I mentioned at the end. The key is the following:
The complete output on
stderr
of the executable is simply split across several objects of typeSystem.Management.Automation.ErrorRecord
. The actual splitting seems to be non deterministic (*). Moreover, the partial strings are stored inside the propertyException
instead ofTargetObject
. Only the firstErrorRecord
has a non-nullTargetObject
.⋮
(*) It depends on the order of write/flush calls of the program in relation to the read calls of the Powershell. If one adds a fflush(stderr) after each fprintf() in my test program below, there will be much more ErrorRecord objects. Except the first one, which seems deterministic, some of them include 2 output lines and some of them 3.
With this, I was able to modify longerr.exe
to also reproduce the bug I alluded to at the end:
class Program
{
static void Main(string[] args)
{
if (args.Length == 1)
{
System.Console.Error.WriteLine(new string('x', int.Parse(args[0])));
}
else
{
for (int i = 0; i < int.Parse(args[1]); i++)
{
System.Console.Error.WriteLine("\n");
System.Console.Error.WriteLine(new string('x', int.Parse(args[0])));
}
}
}
}
Here's the PowerShell script which works (and efficiently):
$p_out = 'success.txt'
$p_err = 'error.txt'
try
{
[Environment]::CurrentDirectory = $PWD
$append = $false
$out = [System.IO.StreamWriter]::new($p_out, $append)
$err = [System.IO.StreamWriter]::new($p_err, $append)
.\longerr.exe 2000 4 2>&1 |
% {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
# https://stackoverflow.com/a/33858097/2328341
if ($_.TargetObject -ne $null) {
$err.WriteLine();
}
$err.Write($_.Exception.Message)
} else {
$out.WriteLine($_)
}
}
}
finally
{
$out.Close()
$err.Close()
}
Notes:
TargetObject
, I would eliminate the main problem I was focusing on in my question, but I would still get the "bug" I mentioned at the end, which the linked SO question addresses.Out-File
(with -NoNewline
) instead of StreamWriter
, but there are two problems with that:
error.txt
and success.txt
).StreamWriter
out-performs Out-File
by over an order of magnitude. For reference, I'm using a Samsung 960 EVO for storage.StreamWriter(string, bool)
constructor writes UTF-8 with no Byte-Order Mark (BOM), while PowerShell 5.1's redirection operators >
and >>
use UTF-16 LE with BOM. For reference, PowerShell 6.0 defaults to UTF-8 with no BOM.(I've included stdout for completeness in real-world situations.) Now, that's an absolutely ridiculous amount of work to get the following functionality of cmd.exe
:
$ .\longerr.exe 2000 4 2>error.txt
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