PowerShell stderr重定向到文件插入换行符

PowerShell stderr重定向到文件插入换行符,powershell,stderr,io-redirection,Powershell,Stderr,Io Redirection,编辑:我为这种行为创建了一个PowerShell UserVoice(反对?);请随意投票 当我重定向到文件时,PowerShell(5.1.16299.98,Windows 10 Pro 10.0.16299)正在向我的stderr中插入换行符,就像为控制台格式化一样。让我们生成任意长度的错误消息: 类程序 { 静态void Main(字符串[]参数) { System.Console.Error.WriteLine(新字符串('x',int.Parse(args[0])); } } 我把上

编辑:我为这种行为创建了一个PowerShell UserVoice(反对?);请随意投票

当我重定向到文件时,PowerShell(5.1.16299.98,Windows 10 Pro 10.0.16299)正在向我的stderr中插入换行符,就像为控制台格式化一样。让我们生成任意长度的错误消息:

类程序
{
静态void Main(字符串[]参数)
{
System.Console.Error.WriteLine(新字符串('x',int.Parse(args[0]));
}
}
我把上面的代码编译成了
longerr.exe
。然后我这样称呼它:

$ .\longerr.exe 60 2>error.txt
        .\longerr.exe $_ 2>&1 |
            % {
                if ($_ -is [System.Management.Automation.ErrorRecord]) {
                    $_.Exception.Message | Out-File -FilePath $f -Append
                }
            }
我在窗口宽度为60的PowerShell控制台中运行了以下脚本:

$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 60 2>error.txt
        .\longerr.exe $_ 2>&1 |
            % {
                if ($_ -is [System.Management.Automation.ErrorRecord]) {
                    $_.Exception.Message | Out-File -FilePath $f -Append
                }
            }
首先,该文件的所有打开和关闭都可能很慢(我知道我可以添加更多代码并使用
StreamWriter
),其次,该方法仍然存在一个问题(bug?),在本问题中我将不进行讨论

为了进行合理性检查,我在
cmd.exe
中运行了
longerr.exe 10002>test.txt
;它没有插入虚假的换行符。

多亏我指出了这个问题,我已经解决了我问题中的两个问题——主要问题和我在最后提到的问题。关键在于:

$ Get-Content $f | Select-String '^(?![+\t]|At line|  )'
可执行文件的
stderr
上的完整输出只需在
System.Management.Automation.ErrorRecord
类型的多个对象之间分割即可。实际拆分似乎是不确定的(*)。此外,部分字符串存储在属性
Exception
中,而不是
TargetObject
中。只有第一个
ErrorRecord
具有非空的
TargetObject

(*)它取决于与Powershell的读取调用相关的程序的写入/刷新调用顺序。如果在下面的测试程序中,在每个fprintf()之后添加一个fflush(stderr),将会有更多的ErrorRecord对象。除了第一条似乎是确定的,其中一些包含2条输出线,另一些包含3条输出线

有了这个,我可以修改
longerr.exe
,也可以重现我最后提到的bug:

类程序
{
静态void Main(字符串[]参数)
{
如果(args.Length==1)
{
System.Console.Error.WriteLine(新字符串('x',int.Parse(args[0]));
}
其他的
{
for(int i=0;i
下面是PowerShell脚本,它可以(高效地)工作:

注:

  • 如果没有对
    TargetObject
    的测试,我将消除我在问题中所关注的主要问题,但我仍然会得到我在最后提到的“bug”,这是链接的SO问题所解决的
  • 我本可以使用
    Out File
    (使用
    -NoNewline
    )而不是
    StreamWriter
    ,但这有两个问题:
  • Windows Defender使所有文件的打开和关闭速度比我关闭“实时保护”(全局或包含
    error.txt
    success.txt
    的目录)时慢了一个数量级
  • 即使没有Defender减慢速度,
    StreamWriter
    的性能也比
    out File
    高出一个数量级。作为参考,我使用了一个存储
  • 写入UTF-8时没有字节顺序标记(BOM),而PowerShell 5.1的重定向操作符
    >
    。PowerShell 6.0默认值仅供参考
  • (为了在现实世界中实现完整性,我加入了stdout。)现在,要获得
    cmd.exe
    的以下功能,这绝对是一个荒谬的工作量:

    $ .\longerr.exe 2000 4 2>error.txt
    

    可能相关:@tessellingheckler:谢谢!这解决了我在问题末尾提到的问题,并且需要切换到一种编程模式,以避免我问题的主要问题。我贴了一个详细的答案。如果你想避免使用
    cmd.exe
    ,这是一个非常丑陋的解决方案,但至少这是一个解释。