.net - 如何在不重定向的情况下读取 Process.StandardOutput ? (F#)

标签 .net f# system.diagnostics processstartinfo redirectstandardoutput

我有这个小函数,可以让我在处理可怕的 System.Diagnostics.Process API 时省去一些麻烦:

let HiddenExec (command: string, arguments: string) =
    let startInfo = new System.Diagnostics.ProcessStartInfo(command)
    startInfo.Arguments <- arguments
    startInfo.UseShellExecute <- false

    startInfo.RedirectStandardError <- true
    startInfo.RedirectStandardOutput <- true

    use proc = System.Diagnostics.Process.Start(startInfo)
    proc.WaitForExit()
    (proc.ExitCode,proc.StandardOutput.ReadToEnd(),proc.StandardError.ReadToEnd())

这非常有效,因为我得到了包含退出代码、stdout 和 stderr 结果的三个元素的元组。

现在,假设我不想“隐藏”执行。也就是说,我想编写一个假设的、更简单的 Exec 函数。那么解决方案是不重定向 stdout/stderr,我们就完成了:

let Exec (command: string, arguments: string) =
    let startInfo = new System.Diagnostics.ProcessStartInfo(command)
    startInfo.Arguments <- arguments
    startInfo.UseShellExecute <- false

    let proc = System.Diagnostics.Process.Start(startInfo)
    proc.WaitForExit()
    proc.ExitCode

但是,如果我可以重构这两个函数以将它们聚合为一个函数,然后向其传递一个“隐藏” bool 标志,那就太好了:

let NewExec (command: string, arguments: string, hidden: bool) =

这边,NewExec(_,_,false)还将返回 stdout、stderr(不仅像以前一样返回 exitCode)。问题是,如果我不进行重定向舞蹈( startInfo.RedirectStandardError <- true ),那么我无法稍后通过 proc.StandardOutput.ReadToEnd() 读取输出。因为我收到错误 StandardOut has not been redirected or the process hasn't started yet .

始终重定向输出的另一个选项,如果传递的隐藏标志不为 true,则调用 Console.WriteLine(eachOutput) ,但这不是很优雅,因为它会一次性写入缓冲区,而不会按照它们出现的正确顺序在屏幕中的 stdout 行之间插入 stderr。对于长时间运行的进程,它将隐藏增量输出,直到进程完成。

那么这里有什么选择呢?我是否需要诉诸使用 Process 中的该死的事件?类(class)? :(

干杯

最佳答案

我会遵循“参数化所有事物”的原则。

在这种情况下,这意味着找到 HiddenExecExec 之间的差异,然后用函数参数化这些差异。

这就是我这样做时的结果:

let ExecWith configureStartInfo returnFromProc (command: string, arguments: string) =
    let startInfo = new System.Diagnostics.ProcessStartInfo(command)
    startInfo.Arguments <- arguments
    startInfo.UseShellExecute <- false

    // parameterize this bit
    configureStartInfo startInfo

    use proc = System.Diagnostics.Process.Start(startInfo)
    proc.WaitForExit()

    // parameterize this bit too
    returnFromProc proc

请注意,通过传入各种 returnFromProc 函数,您可以根据需要更改返回值的类型。

现在您可以定义 HiddenExec 来指定重定向和三元组返回值,就像您最初所做的那样:

/// Specialize ExecWith to redirect the output.
/// Return the exit code and the output and error.
/// Signature: string * string -> int * string * string
let HiddenExec =

    let configureStartInfo (startInfo: System.Diagnostics.ProcessStartInfo) =
        startInfo.RedirectStandardError <- true
        startInfo.RedirectStandardOutput <- true

    let returnFromProc (proc:System.Diagnostics.Process) =       
        (proc.ExitCode,proc.StandardOutput.ReadToEnd(),proc.StandardError.ReadToEnd())

    // partial application -- the command & arguments are passed later
    ExecWith configureStartInfo returnFromProc 

签名表明我们已经得到了我们想要的:您传递一个命令和参数元组并获得 3 元组作为返回:

val HiddenExec : string * string -> int * string * string

请注意,我在这里使用部分应用程序。我还可以使用如下显式参数定义 HiddenExec:

let HiddenExec (command, arguments) =  // (command, arguments) passed here

    let configureStartInfo ...
    let returnFromProc ...

    ExecWith configureStartInfo returnFromProc (command, arguments) // (command, arguments) passed here

同样,您可以将 Exec 定义为使用重定向,如下所示:

/// Specialize ExecWith to not redirect the output.
/// Return the exit code.
/// Signature: string * string -> int
let Exec =

    let configureStartInfo _  =
        ()  // ignore the input

    let returnFromProc (proc:System.Diagnostics.Process) = 
        proc.ExitCode    

    ExecWith configureStartInfo returnFromProc

    // alternative version using `ignore` and lambda
    // ExecWith ignore (fun proc -> proc.ExitCode)    

签名再次表明我们拥有我们想要的更简单的版本:您传递命令和参数元组并仅获取 ExitCode 作为返回:

val Exec : string * string -> int 

关于.net - 如何在不重定向的情况下读取 Process.StandardOutput ? (F#),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38459064/

相关文章:

f# - FsUnit 和检查 float 的相等性

c# - 在配置中指定条件跟踪监听器

c# - 如何从命令行运行 mstest dll

.net - 导出类中的 std::Vector 成员

string.Padding() 的 C# 控制台文本对齐问题

wpf - F# 为 WPF 应用程序保留一些用户值

azure - F# 和 Windows Azure

c# - 使用 System.Diagnostic Process 时,我会错过进程开始和捕获输出开始之间的一些输出行吗?

c# - 愚弄 System.Diagnostics.Process WaitForExit()

c# - .net 4.5 中异步和同步的区别