如何将标准输出保存到文本文件中?

4
我有一个程序,利用第三方命令行工具生成日志。第三方将所有输出生成到STDOUT,用户必须使用“>test.txt”命令保存文件。
然而,该程序以某种方式只生成了一定量的报告,而不是整个报告。这可以通过使用以下命令进行测试
C:\Test\ftk\ripxp>ripxp.exe -r C:\test\ftk\ntuser.dat -d "C:\System Volume\_rest ore{BB12863B-2C77-46C9-BCDA-1810E52F1089}" -p runmru > C:\test\test05.txt
在命令行控制台上运行时可以正常工作,但在程序中只部分工作。
错误被缩小为参数错误或文件保存错误部分(streamReader)。因此,错误可能是由于未正确保存STDOUT造成的。
因此,请问能否提供相关代码?谢谢!
第三方工具(2008 H. Carvey)的参数:
RipXP v.20081001 - CLI RegRipper tool
RipXP [-r Reg hive file] [-p plugin module][-d RP dir][-lgh]
Parse Windows Registry files, using either a single module from the plugins folder.
Then parse all corresponding hive files from the XP Restore Points (extracted from
image) using the same plugin.

-r Reg hive file...Registry hive file to parse
-g ................Guess the hive file (experimental)
-d RP directory....Path to the Restore Point directory
-p plugin module...use only this module
-l ................list all plugins
-h.................Help (print this information)

Ex: C:\>rip -g
C:\>rip -r d:\cases\ntuser.dat -d d:\cases\svi -p userassist

All output goes to STDOUT; use redirection (ie, > or >>) to output to a file.

copyright 2008 H. Carvey

代码:

代码:

static void Main(string[] args)
    {
        // Automatically finds folder that starts with _restore
        DirectoryInfo directoryInfo = new DirectoryInfo(@"C:\System Volume\");
        DirectoryInfo restoreFolder = directoryInfo.GetDirectories().FirstOrDefault(d => d.Name.StartsWith("_restore"));

        // Gets the folder name
        String baka = restoreFolder.Name;

        if (restoreFolder == null)
            throw new DirectoryNotFoundException();

            Process process = new Process();
            process.StartInfo.FileName = @"C:\test\ftk\ripxp\ripxp.exe";
            process.StartInfo.Arguments = @"-r C:\test\ftk\ntuser.dat -d C:\System Volume\" + restoreFolder.Name + " -p runmru";
            process.StartInfo.CreateNoWindow = false;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardError = true;
            process.Start();

            String text = @"-r C:\test\ftk\ntuser.dat -d C:\System Volume\" +restoreFolder.Name  + " -p runmru";
            Console.WriteLine(baka);
            Console.WriteLine(text);

            // Strangely the program only outputs the first section "-r C:\test\ftk\ntuser.dat" argument results.....
            System.IO.StreamReader reader = process.StandardOutput;
            String sRes = reader.ReadToEnd();
            StreamWriter SW;
            SW = File.CreateText(@"C:\test\test01.txt");
            SW.WriteLine(sRes);
            SW.Close();
            Console.WriteLine("File Created Successfully");
            reader.Close();

    }

如果在 Console.WriteLine(text); 后面添加 Console.Out.Flush();,那么你能看到整行输出吗? - SwDevMan81
如果在 Console.Out.Flush(); 之后放置了另一个 Console.WriteLine(text);,那么文本就可以被看到。 - JavaNoob
4个回答

5

你是在等待子进程完成吗?看起来你开始过早地读取其输出。可以这样做:

process.Start();
process.WaitForExit();

此外,你可以在输出完成之前通过委托开始接收输出。像这样(每行文本都会调用委托):
process.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
    // Store e.Data somewhere.
};

即使子进程没有完成,输出也应该完全保存,因为它只是一个5kb的文件。 - JavaNoob
1
无论它有多小,当您过早调用reader.ReadToEnd()时,将只有整个输出的一小部分。 - detunized

3
您可以让.NET在进程退出时通知您,然后读取输出。因为,正如detunized在他的回答+评论中提到的那样,如果在完成之前调用reader.ReadToEnd(),则不会获取所有输出。如果您考虑一下,这是相当明显的-数据尚未生成,因此您如何指望读者读取它呢?
通过使用事件,您将不会阻塞启动线程的方法,这对于具有GUI应用程序且不希望在子进程运行时冻结用户界面非常有用。
// tell it to raise events and hook up a callback to when it completes
process.EnableRaisingEvents = true;
process.Exited += process_Exited;
process.Start();

该方法将在进程完成时调用:
void process_Exited(object sender, EventArgs e)
{
    var process = (Process)sender;
    using (var f = File.CreateText(@"..."))
    {
        f.WriteLine(process.StandardOutput.ReadToEnd());
    }
}

3

System.Console类有一个SetOut方法,可以满足您的需求。

在程序执行开始时,应调用Console.SetOut(yourTextWriter)

只需要在程序的第一行添加这个:

Console.SetOut(File.CreateText(locationToSaveLogs));

2
这会重定向你自己的输出,而不是你启动的进程的输出。 - Hans Passant
@Hans,是的。我认为OP遇到的问题是将输出重定向到文件上。这样做可以使他在运行程序时不需要重定向输出。 - jjnguy
@Patrick。你是正确的。虽然如此,这仍然是很好的知识。所以我会保留它。 - jjnguy
@jjnguy 谢谢 - 这正是我在找到这个问题时所需要的信息! - Tim Lovell-Smith
1
实际上,我发现了一些有趣的东西来回答这个问题。它并没有真正重定向标准输出。它重定向了.net控制台类输出。这是两件不同的事情,例如,如果您从非托管代码中执行printf,则Console.SetOut()无效-printf仍将打印到控制台。(对我来说很不幸 :)) - Tim Lovell-Smith
显示剩余3条评论

0
你需要使用OutputDataReceived事件。这篇MSDN文章解释了如何实现:

https://msdn.microsoft.com/en-us/library/system.diagnostics.process.outputdatareceived(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2

在处理程序内部,使用StreamWriter将输出写入文件中,如下所示:
    StreamWriter sw = new StreamWriter(sOutputFilePath);
    Process process = new Process();
    process.StartInfo.FileName = "ipconfig.exe";
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
    {
        if (!String.IsNullOrEmpty(e.Data))
        {
            sw.WriteLine(e.Data);
        }
    });

    process.Start();
    process.BeginOutputReadLine();
    process.WaitForExit();
    sw.Close();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接