72

I need to collect the standard output and error log from several processes into one single log file.

So every output must append to this log file.

I want to call all the jobs with lines like this:

$p=start-process myjob.bat -redirectstandardoutput $logfile -redirecterroroutput $logfile -wait 

Where do I have to put the information to append?

4 Answers 4

105

In order to append to a file you'll need to use a slightly different approach. You can still redirect an individual process' standard error and standard output to a file, but in order to append it to a file you'll need to do one of these things:

  1. Read the stdout/stderr file contents created by Start-Process
  2. Not use Start-Process and use the call operator, &
  3. Not use Start-Process and start the process with .NET objects

The first way would look like this:

$myLog = "C:\File.log" $stdErrLog = "C:\stderr.log" $stdOutLog = "C:\stdout.log" Start-Process -File myjob.bat -RedirectStandardOutput $stdOutLog -RedirectStandardError $stdErrLog -wait Get-Content $stdErrLog, $stdOutLog | Out-File $myLog -Append 

The second way would look like this:

& myjob.bat 2>&1 >> C:\MyLog.txt 

Or this:

& myjob.bat 2>&1 | Out-File C:\MyLog.txt -Append 

The third way:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = "myjob.bat" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = "" $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo $p.Start() | Out-Null $p.WaitForExit() $output = $p.StandardOutput.ReadToEnd() $output += $p.StandardError.ReadToEnd() $output | Out-File $myLog -Append 
Sign up to request clarification or add additional context in comments.

3 Comments

Does it works for Powershell Remoting? I have testing, I think only the second way works for PS Remoting.
The third way causes a deadlock in some circumstances, read the MSDN or discussion here stackoverflow.com/a/8762068/151641
Excellent! 2>&1 | Out-File $file -Append -Encoding UTF8 is afaik the only way to redirect both stdout and stderr and use UTF-8 output.
24

Andy gave me some good pointers, but I wanted to do it in an even cleaner way. Not to mention that with the 2>&1 >> method PowerShell complained to me about the log file being accessed by another process, i.e. both stderr and stdout trying to lock the file for access, I guess. So here's how I worked it around.

First let's generate a nice filename, but that's really just for being pedantic:

$name = "sync_common" $currdate = get-date -f yyyy-MM-dd $logfile = "c:\scripts\$name\log\$name-$currdate.txt" 

And here's where the trick begins:

start-transcript -append -path $logfile write-output "starting sync" robocopy /mir /copyall S:\common \\10.0.0.2\common 2>&1 | Write-Output some_other.exe /exeparams 2>&1 | Write-Output ... write-output "ending sync" stop-transcript 

With start-transcript and stop-transcript you can redirect ALL output of PowerShell commands to a single file, but it doesn't work correctly with external commands. So let's just redirect all the output of those to the stdout of PS and let transcript do the rest.

In fact, I have no idea why the MS engineers say they haven't fixed this yet "due to the high cost and technical complexities involved" when it can be worked around in such a simple way.

Either way, running every single command with start-process is a huge clutter IMHO, but with this method, all you gotta do is append the 2>&1 | Write-Output code to each line which runs external commands.

2 Comments

Because it can't, it depends precisely on what the program you called does. In this case "robocopy" works, that's not true for everything.
Dead link. I hate Microsoft.
24

Like Unix shells, PowerShell supports > redirects with most of the variations known from Unix, including 2>&1 (though weirdly, order doesn't matter - 2>&1 > file works just like the normal > file 2>&1).

Like most modern Unix shells, PowerShell also has a shortcut for redirecting both standard error and standard output to the same device, though unlike other redirection shortcuts that follow pretty much the Unix convention, the capture all shortcut uses a new sigil and is written like so: *>.

So your implementation might be:

& myjob.bat *>> $logfile 

Comments

0

Maybe it is not quite as elegant, but the following might also work. I suspect asynchronously this would not be a good solution.

$p = Start-Process myjob.bat -redirectstandardoutput $logtempfile -redirecterroroutput $logtempfile -wait add-content $logfile (get-content $logtempfile) 

2 Comments

It doesn't actually work because Powershell doesn't allow you to redirectstandardouput and redirecterroroutput to the same file. "Start-Process : This command cannot be run because "RedirectStandardOutput" and "RedirectStandardError" are same. Give different inputs and Run your command again." is the error I got when I tried this.
I think the poster meant to use $logFile for -redirectstandardoutput. The exe would write to the two files and then the second line appends one file to the other. That would work. But, it does not collate the lines; just appends. Often an exe writes to err only at the end of a run so that would often work. But not in the general case.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.