我有一个C#类库,该库提供了许多可以从PowerShell脚本(.PS1)和高级模块(.PSM1)调用的接口。我有一个静态方法,可以使用System.Console
类将详细信息和调试消息写入控制台:
public class Write
{
public static void Verbose(string msg, string source)
{
if (Config.EnableVerbose)
{
ConsoleColor originalForeGroundColor = Console.ForegroundColor;
ConsoleColor originalBackGroundColor = Console.BackgroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write("VERBOSE: {0} {1}{2}", source, msg, Environment.NewLine);
Console.ForegroundColor = originalForeGroundColor;
Console.BackgroundColor = originalBackGroundColor;
}
}
}
但是,当这些消息显示在PowerShell控制台中时,无法使用重定向来捕获它们,例如使用Out-File
,>&0
甚至使用Start-Transcript
。
我已阅读about_Redirection
,并且使用redirect修饰符无法捕获控制台输出。例如,使用PowerShell高级功能(又名Cmdlet),我写了:
Get-CommandTrace -ScriptBlock { Get-Resource } *> C:\Temp\capture.log
Get-CommandTrace
Cmdlet在执行ScriptBlock期间设置$VerbosePreference = 'Continue'
,并从那里的Get-Resource
输出中捕获详细信息。但是不会从我的C#库中捕获控制台输出。
所以,我的问题很简单:既不是Cmdlet类,也不是继承类的C#类是否能够将输出写入从中调用它的现有PowerShell Runspace?
答案 0 :(得分:2)
注意:
挑战在于写入调用管道的输出流(如您所见,通过Console
进行的写入与PowerShell的输出流无关,并直接打印到控制台)。
虽然我能够获得对调用 runspace 的引用,但是我不知道如何从运行管道中获得一个,所以我能做到最好想到的是创建一个嵌套管道,该管道部分起作用,但以下代码的注释中讨论了限制:
Add-Type -TypeDefinition @'
using System.Management.Automation;
using System.Management.Automation.Runspaces;
public class Write
{
public static void Verbose(string msg)
{
// Create a nested pipeline in the current runspace and
// call Write-Verbose in it.
var rs = Runspace.DefaultRunspace
.CreateNestedPipeline("Write-Verbose \"" + msg.Replace("\"", "\"\"") + "\"", false)
.Invoke();
}
}
'@
$VerbosePreference = 'Continue'
# Regular output to the verbose stream.
Write-Verbose 'msg1'
# Verbose output via the custom type.
# !! This can NOT be redirected from the outside.
[Write]::Verbose('msg2')
# !! SUPPRESSING or REDIRECTING TO A FILE only works
# !! when DIRECTLY APPLIED to the method call.
[Write]::Verbose('msg3') 4> $null
[Write]::Verbose('msg4') 4> t.txt
# !! REDIRECTING TO THE STANDARD OUTPUT STREAM (1) for the OUTSIDE works,
# !! but obviously it then merges with success output.
[Write]::Verbose('msg5') 4>&1
# !! To REDIRECT TO THE STANDARD OUTPUT STREAM (1) and capture the result
# !! INSIDE your script, invoke the method call in (...) or $(...) or @(...)
$out = ([Write]::Verbose('msg6') 4>&1)
"[$out]"
答案 1 :(得分:0)
尽管上面来自mklement0的答案是一个很好的答案,但是如果您不建议使用CreateNestedPipeline
API,则尝试将其与PowerShellCore配合使用或将.NetStandard 2.0用作目标时,它将无法正常工作。 (请参阅PowerShellStandard GitHub存储库上的this线程。)
因此,我有以下工作代码:
Add-Type -TypeDefinition @'
using System.Management.Automation;
using System.Management.Automation.Runspaces;
public class Write
{
public static void Verbose(string msg)
{
using (PowerShell initialPowerShell = PowerShell.Create(RunspaceMode.CurrentRunspace))
{
initialPowerShell.Commands.AddScript("Write-Verbose " + msg.Replace("\"", "\"\"") + "\" -v");
initialPowerShell.Invoke();
}
}
}
'@
$VerbosePreference = 'Continue'
# Regular output to the verbose stream.
Write-Verbose 'msg1'
# Verbose output via the custom type.
# !! This can NOT be redirected from the outside.
[Write]::Verbose('msg2')
# !! SUPPRESSING or REDIRECTING TO A FILE only works
# !! when DIRECTLY APPLIED to the method call.
[Write]::Verbose('msg3') 4> $null
[Write]::Verbose('msg4') 4> t.txt
# !! REDIRECTING TO THE STANDARD OUTPUT STREAM (1) for the OUTSIDE works,
# !! but obviously it then merges with success output.
[Write]::Verbose('msg5') 4>&1
# !! To REDIRECT TO THE STANDARD OUTPUT STREAM (1) and capture the result
# !! INSIDE your script, invoke the method call in (...) or $(...) or @(...)
$out = ([Write]::Verbose('msg6') 4>&1)
"[$out]"
可与Windows的PowerShell 5.1,Windows和Linux的PowerShell Core 6.2.2(Ubuntu / Debian)一起使用。
我仍然会将mklement0答复标记为原始问题的答案。我只是基于过去几天有关将类库迁移到.NetStandard 2.0的研究而编写的另一种。