编辑:我创建了一个PowerShell UserVoice "suggestion"用于(反对?)此行为;随时欢呼。
当我重定向到文件时,PowerShell(5.1.16299.98,Windows 10 Pro 10.0.16299)正在将新行插入到我的stderr中 - 就像为控制台格式化一样。让我们生成任意长度的错误消息:
class Program
{
static void Main(string[] args)
{
System.Console.Error.WriteLine(new string('x', int.Parse(args[0])));
}
}
我将上述内容汇编为longerr.exe
。然后我称之为:
$ .\longerr.exe 60 2>error.txt
我在PowerShell控制台中运行了以下脚本,窗口宽度为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
}
现在,在更广泛的控制台中,我运行了以下内容:
$ Get-Content $f | Select-String '^(?![+\t]|At line| )'
(我本可以在文本编辑器中打开文件并修剪线条。)这是输出:
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
为什么PowerShell会这样做?我可以让它停下来吗?我宁愿不必做这样的事情:
.\longerr.exe $_ 2>&1 |
% {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
$_.Exception.Message | Out-File -FilePath $f -Append
}
}
首先,该文件的所有打开和关闭都很慢(我知道我可以添加更多代码并使用StreamWriter
),其次,仍然存在问题( bug?)用这种方法,我不会在这个问题中讨论。
为了进行健全性检查,我在longerr.exe 1000 2>test.txt
中运行了cmd.exe
;它没有插入虚假的换行符。
答案 0 :(得分:1)
感谢TessellatingHeckler's comment向我指出问题Why does PowerShell chops message on stderr?,我已经能够解决我的问题中的两个问题 - 主要问题和最后提到的问题。关键是以下几点:
可执行文件
stderr
上的完整输出只是分散在System.Management.Automation.ErrorRecord
类型的多个对象上。实际拆分似乎是非确定性的(*)。此外,部分字符串存储在属性Exception
内,而不是TargetObject
。只有第一个ErrorRecord
具有非空TargetObject
。⋮
(*)它取决于程序的写入/刷新调用与Powershell的读取调用的顺序。如果在我的测试程序中的每个fprintf()之后添加一个fflush(stderr),将会有更多的ErrorRecord对象。除了第一个似乎是确定性的,它们中的一些包括2个输出线,其中一些包含3个。
有了这个,我能够修改longerr.exe
以重现我最后提到的错误:
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])));
}
}
}
}
以下是适用(并且有效)的PowerShell脚本:
$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()
}
注意:
TargetObject
的测试,我会消除我在问题中关注的主要问题,但我仍然会得到最后提到的“错误”,链接的SO问题解决了这个问题。 / LI>
Out-File
(-NoNewline
}代替StreamWriter
,但有两个问题:
error.txt
和success.txt
的目录)慢一个数量级StreamWriter
超过Out-File
超过一个数量级。作为参考,我使用Samsung 960 EVO进行存储。StreamWriter(string, bool)
constructor写入没有字节顺序标记(BOM)的UTF-8,而PowerShell 5.1的重定向操作符>
和>>
use UTF-16 LE with BOM。作为参考,PowerShell 6.0默认为to UTF-8 with no BOM。(我在实际情况中包含了stdout的完整性。)现在,这是绝对荒谬的工作量,以获得cmd.exe
的以下功能:
$ .\longerr.exe 2000 4 2>error.txt