PowerShell:为什么我可以';有时会重定向外部方法的stdout和stderr吗?
我在重定向非.NET程序集方法调用的输出时遇到问题: 在下面的代码中,您可以看到一个成功的.NET类System.NET.Dns重定向和两个失败的重定向 一个是内联C#类型,另一个是VS compiled.dll,它只包含与$cs#u代码块相同的内容 到目前为止,我唯一的解决方法是使用[Console]::SetOut和[Console]::SetError捕捉它们的输出 但是为什么它们会失败?我如何重定向/捕获这些流 输出PowerShell:为什么我可以';有时会重定向外部方法的stdout和stderr吗?,powershell,redirect,stdout,stderr,Powershell,Redirect,Stdout,Stderr,我在重定向非.NET程序集方法调用的输出时遇到问题: 在下面的代码中,您可以看到一个成功的.NET类System.NET.Dns重定向和两个失败的重定向 一个是内联C#类型,另一个是VS compiled.dll,它只包含与$cs#u代码块相同的内容 到目前为止,我唯一的解决方法是使用[Console]::SetOut和[Console]::SetError捕捉它们的输出 但是为什么它们会失败?我如何重定向/捕获这些流 输出 tl;博士: 如果要从通过[Console]API生成输出的进程内代
tl;博士:
- 如果要从通过
API生成输出的进程内代码中捕获输出,则必须通过您在问题中提到的[Console]
和[Console]::SetOut()
使用显式重定向[Console]::SetError()
- 请参见下文,了解为什么需要这样做
PowerShell仅允许捕获/重定向标准的
stdout
和stderr
外部(控制台)程序流,对于基于.NET的程序,在这些程序中,console.WriteLine()
和console.Out.WriteLine()
写入stdout
,和Console.Error.WriteLine()
写入stderr
在控制台窗口中运行时,默认情况下,PowerShell会将外部程序的stdout和stderr流传递到控制台(屏幕);相反,ISE将stderr输出发送到PowerShell的错误流[1]
或1>
重定向外部程序的标准输出(指向文件或$null
以抑制它),2>
重定向标准输出[2]。此外,将外部程序的输出分配给变量会捕获其标准输出,通过管道发送外部程序的输出会将其标准输出重定向到PowerShell的成功输出流
相比之下,您使用的是进程中的
[Console]
类型的输出方法,在这种情况下不可能捕获,因为这种方法只调用输出到PowerShell本身运行的同一控制台,而PowerShell不知道它。[3]
您可以删除中间人以验证此行为:
PS> [Console]::WriteLine('hi') *> $null # Try to suppress ALL output streams.
hi # !! Still prints to the console - PowerShell streams were bypassed.
(暂时)重定向[Console]
进程中输出的唯一方法是显式调用和,如问题中所述
&{[System.Net.Dns]::Resolve('bla')}2>$NULL
中的2>$NULL
之所以有效,是因为该方法引发异常,PowerShell将异常输出到其错误流(流号2
),其输出2>$NULL
有效抑制。注意,由于抛出异常,
2>$NULL
仅在方法调用包含在&{…}
中时有效;否则,异常也会终止重定向本身。然而,对于进程中的
[Console]
行为(不涉及任何异常),是否涉及&{…}
没有区别
因此,为了使自定义C#方法与PowerShell的流集成(除了在C#代码中直接使用PowerShell API),请执行以下操作:
- 使用
获取应进入PowerShell成功流的内容(流编号return
)1
- 为应转至PowerShell错误流的内容引发异常(流编号
),但请注意,默认情况下,未经处理的异常将作为一个整体中止封闭语句2
或者,将您的C#代码编译为外部程序,例如,
godemo.exe
:
# With an external executable, redirections work as expected.
godemo.exe 1> $null 2> $null
[1] ISE中的这种分歧行为是有问题的;这一点在本文中进行了讨论 [2] 如果
$ErrorActionPreference='Stop'
恰好生效,则任何2>
重定向都会意外导致脚本终止错误;中讨论了这种有问题的行为
[3] 这些方法写入当前控制台的
stdout
和stderr
流,在没有外部重定向的情况下,这些流会打印到屏幕上。PowerShell引擎对console.Out
或console.Error
一无所知,这与当前目录的情况非常相似。存在powershell引擎的当前目录/路径,可以使用set-Location(别名cd)进行设置,但也存在powershell进程的当前目录,可通过[Environment]::CurrentDirectory.访问。涉及文件的NET调用使用进程的当前目录,而不是powershell引擎的当前目录。@mikez:不,这里的问题是进程内与外部程序,而不是powershell与..NET:使用[Console]
API进程内只是打印到控制台,同一进程中没有其他代码—无论是C#还是PowerShell—看到该输出,除非通过.SetOut()
/和.SetError()
方法应用了显式重定向。我只使用&{}包装调用,因为它被用作解决类似问题的解决方法。@andiDo:white&{…}
仅在涉及异常时才起作用(System.Net.Dns]::Resolve('bla')
,在您的示例中);对于[控制台]::WriteLine()
。。。没关系。请查看我的更新。我不知道进程中捕获的异常。所以.exe中的Console.WriteLine()会写入stdout,但在.dll中使用时不会,对吗?log4net需要它,它使用控制台调用来获取内部调试消息,我想捕获它们。然后我只需要使用SetOut()和SetError()来捕获库。很高兴知道。非常好的回答,顺便说一句。谢谢,mklement0@mklement0考虑到这一点,马
# With an external executable, redirections work as expected.
godemo.exe 1> $null 2> $null