【问题标题】:Powershell -> exe -> powershell Write-Host Console Logging FailingPowershell -> exe -> powershell 写入主机控制台日志记录失败
【发布时间】:2020-01-22 21:30:34
【问题描述】:

TLDW:为了支持特定进程,我需要从一个 powershell 实例/模块启动一个已知的安全可执行文件以用于某些 c# 魔法,然后需要在退出之前启动并执行一个 powershell 脚本。

Powershell 入口点 { 已知的好exe { Powershell 工作要做 } }

现在理想情况下,这将全部从单个控制台实例运行,以便所有输出都易于查看。使用 powershell.Streams 时,exe -> powershell 日志记录一切正常,符合预期。powershell 中的写入主机都显示在控制台中,我得到了我想要的所有信息。

powerShell.Streams.Information.DataAdded += LogMessage;

引入外部powershell模块时出现问题。这个是必需的,因为它运行的父进程和执行环境是 powershell。一旦从 powershell 实例中启动整个堆栈,我就会从外部 powershell 和 exe 获得控制台日志记录。但是内部 powershell 模块中的所有写入主机都消失了。

我已尝试禁用流重定向以及其他一些操作,但这并没有以我希望的方式解决。我希望有人知道是否有办法让它工作,因为它可以解决很多问题。

PowerShell 外部:

$C#Exe = Join-Path $PSScriptRoot $C#ExePath
$C#Args = @()


Write-Host "Hello world" # will show up

& $C#Exe  $C#Args

C# 执行代码:

public static void Main(string[] args)
{
    Console.WriteLine("Hello world"); #Will show up

    var powerShell = PowerShell.Create().AddScript("PowerShellInner.ps1");

    powerShell.Streams.Information.DataAdded += LogMessage<InformationRecord>;
    powerShell.Streams.Warning.DataAdded += LogMessage<WarningRecord>;
    powerShell.Streams.Error.DataAdded += LogMessage<ErrorRecord>;
    powerShell.Streams.Verbose.DataAdded += LogMessage<VerboseRecord>;
    powerShell.Streams.Debug.DataAdded += LogMessage<DebugRecord>;


    StringBuilder resultString = new StringBuilder();

    foreach (dynamic item in powerShell.Invoke().ToList())
    {
        resultString.AppendLine(item.ToString());
    }

    Console.WriteLine(resultString.ToString());

}

private static void LogMessage<T>(object sender, DataAddedEventArgs e)
{
    var data = (sender as PSDataCollection<T>)[e.Index];
    Console.WriteLine($"[{typeof(T).Name}] {Convert.ToString(data)}");
}

PowerShell 内部:

Write-Host "Hello world" #Wont show up

【问题讨论】:

  • 不,也许要澄清一下,我真正的问题是为什么使用外部 powershell 运行它会导致 c# ps 流重定向失败?
  • 那么,您有一个调用 exe(用 C# 编写)的 powershell 脚本,而 C# 程序又在执行一个 powershell 脚本?我很困惑,因为当你说“Powershell”时,我不确定你是指正在执行 exe 的 PS,还是指正在执行 exe 的 PS
  • @Nova 您在调用堆栈的摘要中完全正确。
  • 那么,您在问题中显示的 PS 是“外部”PS,是吗?

标签: c# powershell logging


【解决方案1】:

2020 年 1 月 22 日更新

我无法完全解释为什么您会遇到您所看到的情况,但以下代码有效。

执行 PowerShellOuter.ps1 的输出

代码

注意事项:

  • 您的 c# 程序未显示任何操作输入参数的代码,
    所以我没有为任何输入参数建模

  • 您误用了 AddScript 方法。它需要脚本的文本,而不是脚本名称

  • 下面的代码假设c# exe和两个PS脚本在同一个文件夹中

  • 在 PowerShellInner.ps1 中,使用写入输出。 write-host 不会将数据输出到 PowerShell 对象流引擎,而是顾名思义,直接写入主机并且不向 PowerShell 引擎发送任何内容,以便稍后在管道中转发给命令。见Write-Output or Write-Host in PowerShell

PowerShellOuter.ps1

cls
$CSharpExe = Join-Path $PSScriptRoot "PowerShellExecutionSample.exe"
Write-Host "Hello from PowerShellOuter.ps1"
&$CSharpExe
pause

PowerShellInner.ps1

Write-Output "Hello from PowerShellInner.ps1"

PowerShellExecutionSample.exe 的代码

using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Management.Automation;

namespace PowerShellExecutionSample
{
    class Program
    {
        static void Main(string[] args)
        {
            PowerShellExecutor t = new PowerShellExecutor();
            t.ExecuteSynchronously();
        }
    }

    /// <summary>
    /// Provides PowerShell script execution examples
    /// </summary>
    class PowerShellExecutor
    {
        public void ExecuteSynchronously()
        {
            using (PowerShell PowerShellInstance = PowerShell.Create())
            {
                //You mis-used the AddScript method. It needs the text of the script, not the script name
                string scriptText = File.ReadAllText(string.Format(@"{0}\PowerShellInner.ps1", Environment.CurrentDirectory));

                PowerShellInstance.AddScript(scriptText);
                Collection <PSObject> PSOutput = PowerShellInstance.Invoke();

                // loop through each output object item
                foreach (PSObject outputItem in PSOutput)
                {
                    // if null object was dumped to the pipeline during the script then a null
                    // object may be present here. check for null to prevent potential NRE.
                    if (outputItem != null)
                    {
                        Console.WriteLine(outputItem.BaseObject.ToString() + "\n");
                    }
                }
            }
        }
    }
}

2020 年 1 月 21 日的原始答案

更新您的问题以分解您的代码有帮助 - 谢谢。

我认为你有两个问题:

1) 修改您的 c# 程序以获取来自 PowerShell Inner 的流,并使您的 c# 程序重新发出来自 PowerShell Inner 输出流的数据。这是我用来破解相同问题的 Microsoft 博客文章:Executing PowerShell scripts from C#

2) 修改您的 PowerShell Outer 以获取来自 c# 程序的流。这是一个似乎可以解决这个问题的博客条目:How to redirect output of console program to a file in PowerShell。其核心是从您的 PowerShell 外部执行以下操作:

cmd /c XXX.exe ^&gt;log.txt 2^&gt;^&amp;1

注意:^ 是真正的反引号

【讨论】:

  • 所以我已经在使用 powershell 流来获取输出,但是我继续并从您的第一个链接的 c# 代码中实现了 PSDataCollection,而没有改变行为。然后我尝试使用第二个建议,但这也没有改变任何东西。此外,如果所有这些日志记录都是实时的,而不是需要在查看之前写入 txt,那就太好了。对我来说,这确实像是同时运行 2 个 powershell 实例的问题。这可能是两个无法通信的不同 psexe?
  • @EthanCriss:查看我更新的工作代码答案
  • 这看起来是 rc。 //你误用了 AddScript 方法。它需要脚本的文本,而不是脚本名称
  • @EthanCriss 当您的 PowerShell 内部脚本使用 write-host 而不是 write-output 时,一切正常吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
  • 2020-08-18
  • 2017-03-29
相关资源
最近更新 更多