【发布时间】:2021-02-15 11:39:34
【问题描述】:
我正在从使用 7zip 提取和压缩档案的 System.Diagnostics.Process 重定向 Process.StandardOutput 和 Process.StandardError,但我无法读取进程的进度。
看起来,7Zip 和其他一些应用程序一样,发出的是退格和删除字符以部分写入一行数据,然后使用退格和删除删除写入的字符以显示进度。我正在尝试从目标进程中读取那些部分行输出,但无法这样做。但是,我的这个假设可能是错误的。
我尝试过的:
var process = new Process
{
StartInfo =
{
FileName = command,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
在上面的代码块之后,我尝试过使用各种读取数据的方法。
我已经尝试过异步事件处理程序:
process.OutputDataReceived += (sender, args) => { Console.WriteLine(args.Data); };
process.ErrorDataReceived += (sender, args) => { Console.WriteLine(args.Data); };
process.BeginOutputReadLine();
process.BeginErrorReadLine();
我尝试过使用 StandardOutput 的异步方法:
while (!process.StandardOutput.EndOfStream)
{
char[] buffer = new char[256];
int read = process.StandardOutput.ReadAsync(buffer, 0, buffer.Length).Result;
Console.Write(buffer, 0, read);
}
和
process.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput());
并尝试使用底层基础流的异步方法。
while (!process.StandardOutput.EndOfStream)
{
byte[] buffer = new byte[256];
int read = process.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length).Result;
string data = Encoding.UTF8.GetString(buffer, 0, read);
Console.Write(data);
}
例如,使用以下命令从终端运行 7Zip:
"c:\program files\7-zip\7z.exe" x -o"C:\target" "K:\Disk_23339.secure.7z"
这显示了直接在命令提示符下运行时的进度输出,每个成功进度的增量都会覆盖前一个:
然后它使用退格字符覆盖之前的进度。
使用Process.Start()运行相同的命令和参数
var process = new Process
{
StartInfo =
{
FileName = "c:\program files\7-zip\7z.exe",
Arguments = 'x -o"C:\target" \"K:\Disk_23339.secure.7z\"",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
process.StandardOutput.BaseStream.CopyToAsync(Console.OpenStandardOutput());
process.WaitForExit();
当运行此程序并尝试读取未从源进程发出且不包含新行(通过换行或回车+换行)的进程字符的重定向标准输出时,将输出到标准输出或System.Diagnostics.Process 的标准错误,因此从未写入控制台。
7zip 当然只是一个例子。许多 PowerShell 和 Python 脚本也会出现此问题。
有没有办法从Process.StandardOutput 和Process.StandardError 读取这些字符。
我不确定,但我认为问题在于底层流读取器一次读取一行并且从不返回这些部分行,因为它们从不包含行结束字符。
【问题讨论】:
-
“无论如何都可以读取这些字符” -- 所有写入流的字符都将通过重定向读取。您可能对这个假设不正确:“许多应用程序......使用我假设的退格和删除字符”。更常见的是写回车而没有换行符(即
'\r'),以便下一个进度输出覆盖前一个。这些字符也会被读取,当然如果你使用StreamReader,它们将被解释为换行符而不是返回给你。如果您没有看到任何进展... -
... 在您捕获的输出中,进程本身更有可能检测到 stdout 和/或 stderr 已被重定向,并且根本没有发出这些消息。也就是说,如果没有可靠地重现您的问题的minimal reproducible example,那么努力尝试回答问题是不可行的。我建议你自己编写一个模拟进度和写入状态的程序,然后看看 that 是如何与你当前的程序一起运行的。
-
我对换行符/换行符的假设可能确实是错误的。但是,我提供了一个使用 7zip 的最小可重现示例,当从终端运行时输出部分行。使用带有重定向输出的相同参数的 Process.Start 会产生问题。
-
“我提供了一个使用 7zip 的最小可重现示例”——要求某人安装 7zip 与 minimal reproducible example 相差远。您不必要地将您的潜在观众限制在极少数人中,并且即使在这些人中也需要对程序实际做什么有一定程度的洞察力,这可能是不可能的。如果您不想接受我的建议,那是您的特权,但当然不要误导自己认为当前提出的问题很有可能得到有用的答案。
-
“我已经解释过了” - 这实际上可能是答案,以及您的其他建议,即目标进程已检测到输出已被重定向。请张贴作为答案。如果确实是这种情况,我会将您的答案标记为正确,这将对遇到相同问题的其他人有用。不用争论。同样,我提供了一个最小的可复制答案。 IMO,您对示例使用 powershell vs 7Zip 提供的建议是非常主观的。
标签: c# terminal process io 7zip