At some stage I decided I need backup plan for my virtual server. After some intensive web surfing I found brilliant command line tool – s3.exe. The next step was to schedule to run it periodically, every night, when everybody is sleeping.
I decided to use PowerShell. It’s new, it’s powerful, it comes pre-installed with Windows Server 2008. PowerShell is very popular among system administrators.
Also, I got an idea to save output of backup application to log file so that I could see results later. And this is where things didn’t work as I expected. Here’s how to redirect console program output to a file:
s3.exe > log.txt
If you run this you will be surprised to find that log.txt is empty. As it turns out s3.exe, being a good Windows citizen, writes errors to STDERR stream. To redirect error output to the same file you need to add magic string 2>&1:
s3.exe 2>&1 > log.txt
Two new problems appear. First, errors appear as exceptions in log file:
s3.exe : s3.exe version 1.6 - check for updates at http://s3.codeplex.com
At PS Logging.ps1:4 char:43
+ s3.exe <<<< 2>&1 > log.txt
+ CategoryInfo : NotSpecified: (s3.exe version ...s3.codeplex.com
:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
PowerShell helpfully wraps STDERR output into exceptions. This is nice but I don’t need it! All I want is to see how a program worked by examining log file. Do this to unwrap exceptions and show plain text:
s3.exe 2>&1 | foreach-object {$_.ToString()} | Out-File log.txt
And second, errors areĀ out of order with normal output. They could appear before or after normal lines emitted by a program, making it hard to diagnose problem. To confirm that, I created a simple console program:
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Hello");
}
Console.Error.WriteLine("Bah bah");
for (int i = 0; i < 10; i++)
{
Console.WriteLine("2");
}
}
If you run this program from PowerShell and redirect output to a file, error message (bah bah) could appear anywhere in the log file:
Hello
Bah bah
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
2
2
2
2
2
2
2
2
2
2
The solution I came up with is to let old reliable cmd.exe to do the work:
cmd /c s3.exe `>log.txt 2`>`&1
Note backsticks – I use them to prevent PowerShell from parsing redirect operators.
Links
PowerShell ABC’s – O is for Output
How to capture exe output to a PowerShell variable
Another approach to unwrap exceptions: use add-content cmdlet.