【问题标题】:PowerShell stderr redirect to file inserts newlinesPowerShell stderr 重定向到文件插入换行符
【发布时间】:2018-07-17 06:14:50
【问题描述】:

编辑:我为(反对?)这种行为创建了一个 PowerShell UserVoice "suggestion";随意点赞。

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

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

我在窗口宽度为 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 $_ 2>&1 |
            % {
                if ($_ -is [System.Management.Automation.ErrorRecord]) {
                    $_.Exception.Message | Out-File -FilePath $f -Append
                }
            }

首先,该文件的所有打开和关闭都可能很慢(我知道我可以添加更多代码并使用StreamWriter),其次,还有一个问题(错误? ) 使用这种方法,我不会在这个问题中讨论。

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

【问题讨论】:

  • @TessellatingHeckler:谢谢!这解决了我在问题末尾提到的问题,并且需要切换到可以避免我问题的主要问题的编程模式。我发布了一个详细说明问题的答案。如果一个人想避免向cmd.exe 发起攻击,这是一个非常丑陋的解决方案,但至少它是一种解释。

标签: powershell stderr io-redirection


【解决方案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()
}

注意事项:

  1. 如果不对 TargetObject 进行测试,我会消除我在问题中关注的主要问题,但我仍然会得到我最后提到的“错误”,链接的 SO 问题解决了这个问题。
  2. 我本可以使用Out-File(和-NoNewline)代替StreamWriter,但这样做有两个问题:
    1. Windows Defender 使所有文件的打开和关闭速度比我关闭“实时保护”时慢一个数量级(全局或在包含 error.txtsuccess.txt 的目录上)。
    2. 即使没有 Defender 减慢速度,StreamWriter 的性能也优于 Out-File 一个数量级以上。作为参考,我使用Samsung 960 EVO 进行存储。
  3. StreamWriter(string, bool) constructor 写入没有字节顺序标记 (BOM) 的 UTF-8,而 PowerShell 5.1 的重定向运算符 &gt;&gt;&gt; use UTF-16 LE with BOM。作为参考,PowerShell 6.0 默认为to UTF-8 with no BOM

(为了在现实世界中的完整性,我已经包含了标准输出。)现在,要获得cmd.exe 的以下功能,这是绝对荒谬的工作量

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-22
    • 2021-06-29
    • 1970-01-01
    • 2014-09-30
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 1970-01-01
    相关资源
    最近更新 更多