【问题标题】:How Do I Redirect Output From Powershell如何重定向 Powershell 的输出
【发布时间】:2012-06-13 12:47:42
【问题描述】:

我有一个类似但不同的问题,例如How to redirect Powershell output from a script run by TaskScheduler and override default width of 80 characters

我有一个很久以前编写的自定义安装程序框架。在其中我可以执行“任务”。我最近不得不添加一个任务来执行一个 PowerShell 脚本。现在,即使任务是用 C# 编写的,我也无法直接调用 PowerShell 脚本中的命令。不幸的是,这不在讨论范围内。

简而言之,我想从 C# 调用 PowerShell 可执行文件并将其输出重定向回我的应用程序。这是我到目前为止所做的:

我可以使用以下代码(来自我创建的测试项目)成功调用 PowerShell:

  string powerShellExeLocation = null;

  RegistryKey localKey = Registry.LocalMachine;

  RegistryKey subKey = localKey.OpenSubKey(@"SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine");
  powerShellExeLocation = subKey.GetValue("ApplicationBase").ToString();

  if (!Directory.Exists(powerShellExeLocation))
    throw new Exception("Cannot locate the PowerShell dir.");

  powerShellExeLocation = Path.Combine(powerShellExeLocation, "powershell.exe");

  if (!File.Exists(powerShellExeLocation))
    throw new Exception("Cannot locate the PowerShell executable.");

  string scriptLocation = Path.Combine(Environment.CurrentDirectory, "PowerShellScript.ps1");

  if (!File.Exists(scriptLocation))
    throw new Exception("Cannot locate the PowerShell script.");

  ProcessStartInfo processInfo = new ProcessStartInfo();
  processInfo.Verb = "runas";
  processInfo.UseShellExecute = false;
  processInfo.RedirectStandardOutput = true;
  processInfo.RedirectStandardOutput = true;
  processInfo.WorkingDirectory = Environment.CurrentDirectory;

  processInfo.FileName = powerShellExeLocation;
  processInfo.Arguments = "-NoLogo -OutputFormat Text -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + scriptLocation + "\" ";

  Process powerShellProcess = new Process();
  powerShellProcess.StartInfo = processInfo;
  powerShellProcess.OutputDataReceived += new DataReceivedEventHandler(powerShellProcess_OutputDataReceived);
  powerShellProcess.ErrorDataReceived += new DataReceivedEventHandler(powerShellProcess_ErrorDataReceived);

  powerShellProcess.Start();

  while (!powerShellProcess.HasExited)
  {
    Thread.Sleep(500);
  }

我的重定向输出处理程序永远不会被调用:

void powerShellProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)

void powerShellProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)

我相当有信心脚本正在运行。现在我只有一个输出 PATH 内容的测试脚本。

$a = $env:path; $a.Split(";")

我在 PowerShell 实例中对此进行了测试,结果正确。

如何让 PowerShell 输出重定向到我的 C# 代码?我不想依赖我将执行的脚本来处理他们自己的日志记录。我宁愿收集他们的输出并在框架的日志记录机制中处理它。

编辑 意识到我在该代码示例中留下了奇怪的循环以等待退出。我已经完成了“WaitForExit”流程:

powerShellProcess.WaitForExit();

编辑
使用@MiniBill 的建议。我想出了以下代码片段:

powerShellProcess.Start();
while (!powerShellProcess.HasExited)
{
  string line;
  while (!string.IsNullOrEmpty((line = powerShellProcess.StandardOutput.ReadLine())))
    this.LogInfo("PowerShell running: " + line);
}

这可以很好地处理输出。它把我的台词减少到 80 个字符 :(,但我可以忍受,除非其他人可以提供解决此问题的建议!

更新解决方案

/*
  * The next few lines define the process start info for the PowerShell executable.
  */
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
processInfo.WorkingDirectory = Environment.CurrentDirectory;

//this FileName was retrieved earlier by looking in the registry for key "HKLM\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" and the value for "ApplicationBase"
processInfo.FileName = powerShellExeLocation;

//if we're going to use script arguments build up the arguments from the process start correctly.
if (!string.IsNullOrEmpty(this.ScriptArguments))
  processInfo.Arguments = "-NoLogo -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + ScriptLocation + "\" '" + ScriptArguments + "'";
else
  processInfo.Arguments = "-NoLogo -NonInteractive -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + ScriptLocation + "\"";

//create the Process object, set the start info, and start the process
Process powerShellProcess = new Process();
powerShellProcess.StartInfo = processInfo;

powerShellProcess.Start();

/*
  * While the PowerShell process hasn't exited do the following:
  * 
  * Read from the current position of the StandardOutput to the end by each line and
  * update the tasks progress with this information
  * 
  * Read from the current position of StandardError to the end by each line, update
  * the task's progress with this information and set that we have an error.
  * 
  * Sleep for 250 milliseconds (1/4 of a sec)
  */
bool isError = false;
while (!powerShellProcess.HasExited)
{
  string standardOuputLine, standardErrorLine;
  while (!string.IsNullOrEmpty((standardOuputLine = powerShellProcess.StandardOutput.ReadLine())))
  {
    this.UpdateTaskProgress(standardOuputLine);
  }


  while (!string.IsNullOrEmpty((standardErrorLine = powerShellProcess.StandardError.ReadLine())))
  {
    this.UpdateTaskProgress(standardErrorLine);
    isError = true;
  }

  Thread.Sleep(250);
}

/*
  * Now that the process has completed read to the end of StandardOutput and StandardError.
  * Update the task progress with this information.
  */

string finalStdOuputLine = powerShellProcess.StandardOutput.ReadToEnd();
string finalStdErrorLine = powerShellProcess.StandardError.ReadToEnd();

if (!string.IsNullOrEmpty(finalStdOuputLine))
  this.UpdateTaskProgress(finalStdOuputLine);

if (!string.IsNullOrEmpty(finalStdErrorLine))
{
  this.UpdateTaskProgress(finalStdErrorLine);
  isError = true;
}

// there was an error during the run of PowerShell that was output to StandardError.  This doesn't necessarily mean that
// the script error'd but there was a problem.  Throw an exception for this.
if (isError)
  throw new Exception(string.Format(CultureInfo.InvariantCulture, "Error during the execution of {0}.", this.ScriptLocation));

【问题讨论】:

  • $Host.UI.RawUI 有一个 BufferSizeWindowSize 属性,即使它没有写入任何可见的东西,它也可能仍然有效。
  • @Kiquenet 我已经提供了我在“任务”结构中使用的解决方案,该结构使用脚本调用 PowerShell 可执行文件并记录输出。 “UpdateTaskProgress”方法只是处理日志记录到我的日志结构并更新我的 UI。
  • 我很确定 Windows 也不允许您跨管理员/非管理员安全边界重定向标准输入/输出/错误。您必须找到一种不同的方法来从以管理员身份运行的程序中获取输出 - 参考:stackoverflow.com/a/8690661
  • 您缺少 process.BeginOutputReadLine();和 process.BeginErrorReadLine();来电见docs.microsoft.com/en-us/dotnet/api/…

标签: c# powershell


【解决方案1】:

您想使用Process.StandardOutput 属性。这是一个流,你可以打开它来获取程序的输出

【讨论】:

  • 当控制台发生输出时,我没有办法得到警报(事件)。似乎如果我使用 StandardOutput 属性,我将不得不等到进程退出。
  • 你可以生成一个新线程,当它找到一些输出时会引发一个事件
  • 您认为像我发布的那样在 HasExited 上执行等待循环是否可以接受?我应该睡在那个循环里还是让它飞起来?
  • 如果你使用循环,你应该明确地休眠,否则你会吸收大量的 CPU 无所事事。 80 字符截断是 powershell iirc 所固有的。你可以使用这个而不是使用循环:msdn.microsoft.com/en-us/library/…
  • 好的,我认为睡觉是个好主意。我只想在 PowerShell 可执行文件将其写入 StandardOut 时获取输出。等到退出以获取所有输出并不是很理想,因为这样做的重点不仅在于将其记录在安装程序框架中,还在于向用户显示当前状态/输出。
猜你喜欢
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 2012-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多