PowerShell stderr 重定向到文件插入换行符
PowerShell stderr redirect to file inserts newlines
编辑:我为(反对?)此行为创建了一个 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
我 运行 在 PowerShell 控制台中使用 window 宽度 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
),其次,还有这种方法仍然是一个问题(错误?),我不会在这个问题中讨论这个问题。
为了完整性检查,我在 cmd.exe
中 运行 longerr.exe 1000 2>test.txt
;它没有插入虚假的换行符。
感谢 pointing me to the question ,我已经能够解决问题中的两个问题——主要问题和我最后提到的问题。关键是:
The complete output on stderr
of the executable is simply split across several objects of type System.Management.Automation.ErrorRecord
. The actual splitting seems to be non deterministic (*). Moreover, the partial strings are stored inside the property Exception
instead of TargetObject
. Only the first ErrorRecord
has a non-null TargetObject
.
⋮
(*) 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.
有了这个,我能够修改 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]) {
#
if ($_.TargetObject -ne $null) {
$err.WriteLine();
}
$err.Write($_.Exception.Message)
} else {
$out.WriteLine($_)
}
}
}
finally
{
$out.Close()
$err.Close()
}
备注:
- 如果没有
TargetObject
的测试,我会消除我在问题中关注的主要问题,但我仍然会得到我最后提到的 "bug",链接的 SO问题地址。
- 我本可以使用
Out-File
(带 -NoNewline
)而不是 StreamWriter
,但这样做有两个问题:
- Windows Defender 使所有文件的打开和关闭速度比我关闭 "Real-time protection" 时慢一个数量级(全局或在包含
error.txt
和 success.txt
).
- 即使没有 Defender 减慢速度,
StreamWriter
的性能也比 Out-File
高出一个数量级以上。作为参考,我使用 Samsung 960 EVO 进行存储。
-
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.
(为了在真实情况下的完整性,我已经包含了标准输出。)现在,绝对荒谬的工作量 来获得 cmd.exe
的以下功能:
$ .\longerr.exe 2000 4 2>error.txt
编辑:我为(反对?)此行为创建了一个 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
我 运行 在 PowerShell 控制台中使用 window 宽度 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
),其次,还有这种方法仍然是一个问题(错误?),我不会在这个问题中讨论这个问题。
为了完整性检查,我在 cmd.exe
中 运行 longerr.exe 1000 2>test.txt
;它没有插入虚假的换行符。
感谢
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.
有了这个,我能够修改 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]) {
#
if ($_.TargetObject -ne $null) {
$err.WriteLine();
}
$err.Write($_.Exception.Message)
} else {
$out.WriteLine($_)
}
}
}
finally
{
$out.Close()
$err.Close()
}
备注:
- 如果没有
TargetObject
的测试,我会消除我在问题中关注的主要问题,但我仍然会得到我最后提到的 "bug",链接的 SO问题地址。 - 我本可以使用
Out-File
(带-NoNewline
)而不是StreamWriter
,但这样做有两个问题:- Windows Defender 使所有文件的打开和关闭速度比我关闭 "Real-time protection" 时慢一个数量级(全局或在包含
error.txt
和success.txt
). - 即使没有 Defender 减慢速度,
StreamWriter
的性能也比Out-File
高出一个数量级以上。作为参考,我使用 Samsung 960 EVO 进行存储。
- Windows Defender 使所有文件的打开和关闭速度比我关闭 "Real-time protection" 时慢一个数量级(全局或在包含
-
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.
(为了在真实情况下的完整性,我已经包含了标准输出。)现在,绝对荒谬的工作量 来获得 cmd.exe
的以下功能:
$ .\longerr.exe 2000 4 2>error.txt