How to save STDOUT outputs to a text file?

11,857

Solution 1

You can have .NET inform you when the process has exited and then read the output. Because, as detunized mentioned in his answer+comment, if you call reader.ReadToEnd() before it has completed, you won't get all the output. If you think of it, it's quite obvious - the data hasn't been produced yet, so how can you expect the reader to read it?

By using events you will not block the method that starts the thread which can be quite useful if you have a GUI application and don't want to freeze the user interface while the child process is running.

// tell it to raise events and hook up a callback to when it completes
process.EnableRaisingEvents = true;
process.Exited += process_Exited;
process.Start();

This method will be called when the process has completed:

void process_Exited(object sender, EventArgs e)
{
    var process = (Process)sender;
    using (var f = File.CreateText(@"..."))
    {
        f.WriteLine(process.StandardOutput.ReadToEnd());
    }
}

Solution 2

Are you waiting for the child process to finish? It looks like you start reading its output too early. It could be done like this:

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

Also, you can start receiving the output via delegates before it finishes. Like this (the delegate is called for each line of text):

process.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
{
    // Store e.Data somewhere.
};

Solution 3

The System.Console class has a SetOut method that should do what you need.

You should call Console.SetOut(yourTextWriter) at the beginning of your program's execution.

Just add this at the first line of the program:

Console.SetOut(File.CreateText(locationToSaveLogs));
Share:
11,857

Related videos on Youtube

JavaNoob
Author by

JavaNoob

Updated on June 04, 2022

Comments

  • JavaNoob
    JavaNoob almost 2 years

    I have a program which utilizes a 3rd party command line tool to generate logs. The 3rd party generates all its output to the STDOUT which the user must then use the " > test.txt" command to save the file.

    However the program somehow generates a certain amount of report generated but not the entire report. This was test by using the command

    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

    on the command line console which works and on the program which works only partially.

    The errors were narrowed down to either the arguments error or the file saving error part (streamReader). Therefore the error could be due to the STDOUT not being properly saved.

    Therefore may someone please advise on the codes? Thanks!

    The Arguments for the 3rd party tool(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
    

    The Codes:

    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();
    
        }
    
  • JavaNoob
    JavaNoob over 13 years
    No even without the child process to be finished, the output should be able to save fully as its only a 5kb file.
  • detunized
    detunized over 13 years
    It doesn't matter how small it is. When you call reader.ReadToEnd() too soon there will be only a small fraction of the whole output.
  • user1703401
    user1703401 over 13 years
    That redirects your own output, not the output of the process that you start.
  • jjnguy
    jjnguy over 13 years
    @Hans, yeah. I thought the problem that the OP was having was with redirecting the output to a file. This will make it so he doesn't need to redirect the output when the program is run.
  • Patrick
    Patrick over 13 years
    @jinguy: Well, it obviously doesn't solve the problem, and is kind of an answer to a question that wasn't asked..
  • jjnguy
    jjnguy over 13 years
    @Patrick. You are correct. It still is good knowledge though. So I'm gonna leave it.
  • Patrick
    Patrick over 13 years
    @jjnguy: I thought Stackoverflow was not a place for "good knowledge" but rather a question-answer forum. Knowing about design patterns, deadlocks and other stuff is also good knowledge, but we normally don't put "answers" about those to every question. ;)
  • jjnguy
    jjnguy over 13 years
    @Patrick, I'm not sure why you are worried. I supplied this answer because I misunderstood the op's problem. But, this could be directly helpful to the op if he wants to redirect the output in the program instead of having do do it from the command line. It's not like I'm trying to teach Ruby here.
  • Tim Lovell-Smith
    Tim Lovell-Smith over 10 years
    @jjnguy Thanks - this is the info I was looking for when I found this question!
  • Tim Lovell-Smith
    Tim Lovell-Smith over 10 years
    Actually found out something interesting for this answer. It doesn't actually redirect standard out. It redirects the .net Console class output. These are two different things, for instance, if you do printf from unmanaged code, Console.SetOut() has no effect - printf still prints to the console. (Unfortunately for me :))

Related