更改“标准错误”到“标准输出”

发布于 2024-11-11 00:41:08 字数 2189 浏览 1 评论 0原文

我有一个写入错误输出的 PowerShell 脚本。该脚本可以简单如下:

Write-Error 'foo'
Start-Sleep -s 5
Write-Error 'bar'

我实际调用的脚本会生成一个外部进程,该进程需要一段时间来处理并写入标准错误。

现在,当我像这样调用脚本时:

. myScript.ps1

我收到带有 PowerShell 通常行为的错误消息(即红色文本和大量调试信息)。由于该文本与我的实际应用程序中的 PowerShell 无关,因此我不需要这些调试信息,它只会使结果的可读性较差(并且无法处理)。

有没有办法将该输出直接重定向到标准输出,以便我只获取文本?

我尝试了这样的事情:

Write-Host ( . myScript.ps1 2>&1 )

但这会延迟输出,直到一切完成。

关于“调试信息”,当我现在运行脚本时,输出如下所示(红色):

C:\path\to\myScript.ps1 : foo
Bei Zeile:1 Zeichen:2
+ . <<<<  'C:\path\to\myScript.ps1'
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,myScript.ps1

C:\path\to\myScript.ps1 : bar
Bei Zeile:1 Zeichen:2
+ . <<<<  'C:\path\to\myScript.ps1'
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,myScript.ps1

当我使用 Write-Host ( . myScript.ps1 2>&1 ) 运行脚本时,其中错误输出写入标准输出,我得到如下结果:

foo bar

这正是我想要的输出,除了 Write-Host (..)使输出仅出现在脚本已终止,因此我无法收到有关该脚本进度的任何信息。

实际上更接近我的实际问题(因为很难用纯 PowerShell 来解释);我有以下 Python 脚本,它与我使用的命令行程序的功能大致相似,即它执行一些处理并将进度打印到标准错误:

#!/usr/bin/python
import sys, time
sys.stderr.write( 'Progressing... ' )
sys.stderr.flush()
time.sleep( 5 )
sys.stderr.write( 'done.\n' )
sys.stderr.flush()

现在,当我使用 python script.py 调用它时/code>,它工作正常并打印出“progressing”行,等待 5 秒,然后将“done”打印到 PowerShell(作为普通文本)。现在的问题是我想在作业中运行它,如下所示:Start-Job { python script.py }

该作业正确启动,并且在后台运行良好,但是当我想通过 Receive-Job检查其进度时,我一开始根本没有任何输出,并且在脚本/程序完成(即 5 秒后),我得到以下输出(再次呈红色):

Progressing... done.
    + CategoryInfo          : NotSpecified: (Progressing... done.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

显然这不是我想要得到的。相反,我想获得打印到标准错误的实际输出,既实时(即当它发生在脚本中时)并且没有与 PowerShell 相关的调试文本。

I have a PowerShell script that writes to the error output. The script can be as simple as the following:

Write-Error 'foo'
Start-Sleep -s 5
Write-Error 'bar'

The script I actually call spawns an external process that takes a while to process and writes to standard error.

Now when I call the script like this:

. myScript.ps1

I get the error message with PowerShell's usual behaviour (i.e. red text and lots of debugging information). As that text has no relation to PowerShell in my actual application, I don't need those debugging information and it only makes the result less readable (and impossible to process).

Is there a way to redirect that output directly into standard output, so that I just get the text?

I tried something like this:

Write-Host ( . myScript.ps1 2>&1 )

But that delays the output until everything is completed.

About the “debugging information”, when I run the script right now, the output looks like this (in red):

C:\path\to\myScript.ps1 : foo
Bei Zeile:1 Zeichen:2
+ . <<<<  'C:\path\to\myScript.ps1'
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,myScript.ps1

C:\path\to\myScript.ps1 : bar
Bei Zeile:1 Zeichen:2
+ . <<<<  'C:\path\to\myScript.ps1'
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,myScript.ps1

When I run the script with Write-Host ( . myScript.ps1 2>&1 ), where the error output is written to the standard output, I get a result like this:

foo bar

That is exactly what I would like the output to be, except that the Write-Host (..) makes the output only appear after the script has terminated, so I cannot receive any information on the progress of said script.

To come actually closer to my actual problem (because it’s hard to explain that with pure PowerShell); I’ve got the following Python script that resembles approximately what the command line program I use does, i.e. it does some processing and prints out the progress to the standard error:

#!/usr/bin/python
import sys, time
sys.stderr.write( 'Progressing... ' )
sys.stderr.flush()
time.sleep( 5 )
sys.stderr.write( 'done.\n' )
sys.stderr.flush()

Now, when I call it with python script.py, it works correctly and prints out the “progressing” line, waits 5 seconds and then prints the “done” to PowerShell (as normal text). The problem is now that I want to run this in a job, like this: Start-Job { python script.py }.

The job gets started correctly, and also works fine in the background, but when I want to check its progress via Receive-Job <id>, I get no output at all at first, and after the script/program finished (i.e. after the 5 seconds), I get the following output (in red again):

Progressing... done.
    + CategoryInfo          : NotSpecified: (Progressing... done.:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

Obviously that is not what I am trying to get. Instead I want to get the actual output that was printed to the standard error, both live (i.e. when it happens in the script) and without that PowerShell related debugging text.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蝶…霜飞 2024-11-18 00:41:08

根据问题的 edit 部分,这应该是合适的:

. MyScript.ps1 2>&1 | %{ Write-Host $_ }

它只写“foo”和“bar”,并且它们一旦发生就会出现。

编辑

实际上,这更简单,也工作得很好:

. MyScript.ps1 2>&1 | Write-Host

但我保留原来的答案。该代码允许动态处理输出($_)并执行其他操作(即不仅仅是将其写入主机)。

编辑 2

写入 STDERR 的外部程序:

using System;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; ++i)
            {
                Console.Error.WriteLine("Step " + i);
                System.Threading.Thread.Sleep(2000);
            }
        }
    }
}

作为作业启动此应用程序并定期接收其输出的脚本。

$ErrorActionPreference = 'continue'

$job = Start-Job { C:\TEMP\Test\ConsoleApplication1.exe 2>&1 }
for(;;) {
    Receive-Job $job | Write-Host
    Start-Sleep 2
}

该脚本完全满足您的需要(对于您的 edit 2 部分):它会在输出可用时立即显示输出,并且不会显示不需要的额外错误信息。

PS 这个版本也有效:

$job = Start-Job { C:\TEMP\Test\ConsoleApplication1.exe }
for(;;) {
    Receive-Job $job 2>&1 | Write-Host
    Start-Sleep 2
}

According to the edit section of the question, this should be suitable:

. MyScript.ps1 2>&1 | %{ Write-Host $_ }

It writes just "foo" and "bar" and they appear as soon as they happen.

EDIT

Actually this is even simpler and works fine, too:

. MyScript.ps1 2>&1 | Write-Host

But I keep the original answer. That code allows to process the output ($_) dynamically and do something else (i.e. not just write it to the host).

EDIT 2

External program that writes to STDERR:

using System;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; ++i)
            {
                Console.Error.WriteLine("Step " + i);
                System.Threading.Thread.Sleep(2000);
            }
        }
    }
}

The script that starts this application as a job and receives its output periodically.

$ErrorActionPreference = 'continue'

$job = Start-Job { C:\TEMP\Test\ConsoleApplication1.exe 2>&1 }
for(;;) {
    Receive-Job $job | Write-Host
    Start-Sleep 2
}

The script does exactly what you need (for your edit 2 section): it shows the output as soon as it is available and it does not show unwanted extra error information.

P.S. This version works, too:

$job = Start-Job { C:\TEMP\Test\ConsoleApplication1.exe }
for(;;) {
    Receive-Job $job 2>&1 | Write-Host
    Start-Sleep 2
}
素衣风尘叹 2024-11-18 00:41:08

尝试:

$erroractionpreference = "silentlycontinue"
.\myscript.ps1

Try:

$erroractionpreference = "silentlycontinue"
.\myscript.ps1
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文