Summary: Microsoft Scripting Guy, Ed Wilson, talks about troubleshooting output from a Windows PowerShell script.
Hey, Scripting Guy! First, I want to say that you guys absolutely rock! I mean, I never miss a single day reading your posts. They are always right on, and it is like somehow you read my mind. I cannot tell you how many times I have read your post in the morning, and then needed to do that very thing in the afternoon. I also know that you probably get hundreds of emails every day, so I don’t really expect an answer, but I thought that maybe...what the heck. So here goes:
I wrote a script that works perfectly. I am trying to reduce our security footprint by stopping services. For some reason, the number of services has exploded between Windows Server 2008 and Windows Server 2012 R2. I think, "The fewer things running, the better." So I am trying to see what I have by doing an audit. I thought if I get a list of what is running and what is not, I could compare them to see what is going on. Here is the script:
$services = Get-WmiObject win32_Service
$running = $services | where { $_.state -eq 'running' }
$stopped = $services | where { $_.state -eq 'stopped' }
Write-Host -ForegroundColor Green "There are " $running.Count " running services. They are here:"
$running | Format-Table name
Write-Host -ForegroundColor Red "There are " $stopped.Count " stopped services. They are here:"
$stopped | Format-Table name
The problem is not with the script, it works perfectly for what I want to do. The problem is that I want to write the data to a text file. Here is what I did, and it sort of works, but not really:
$file = "c:\fso\MyServiceStatus.txt"
$services = Get-WmiObject win32_Service
$running = $services | where { $_.state -eq 'running' }
$stopped = $services | where { $_.state -eq 'stopped' }
Write-Host -ForegroundColor Green "There are " $running.Count " running services. They are here:" |
Out-file $file -Append
$running | Format-Table name |
Out-file $file -Append
Write-Host -ForegroundColor Red "There are " $stopped.Count " stopped services. They are here:" |
Out-file $file -Append
$stopped | Format-Table name |
Out-file $file -Append
When I say it sort of works, I mean that it creates the text file, and it writes all of the service names to the file. But I do not get my header information. So I cannot really tell which services are running and which are stopped. It is really weird, and I am wondering if I have found a bug in Windows PowerShell. If you don't have an answer, maybe you can forward this to someone who can look at it. Thanks again for all you do.
—BP
Hello BP,
Microsoft Scripting Guy, Ed Wilson, is here. This morning I am sitting in the living room sipping a cup of Earle Grey tea with a bit of jasmine in it. I am watching the rain coming down, and looking over my scripter@microsoft.com email.
BP, yes, I do get a lot of email. I also enjoy answering some of it. I am glad you enjoy reading my column and that you have found it useful. I am especially pleased that you are writing your own scripts and using Windows PowerShell to solve some of your issues.
Understanding different output streams
One concept that is a bit difficult to understand in Windows PowerShell is the concept of output streams. Windows PowerShell has several output streams. This can be especially useful for things like trying to redirect errors, verbose output, debug messages, or even warning messages. However, it is not useful for redirecting the Write-Host cmdlet.
Note For a good discussion about the various output streams and redirecting output, see Understanding Streams, Redirection, and Write-Host in PowerShell, which was written by Honorary Scripting Guy, June Blender.
Basically what happens when I use Write-Host is that it goes only to the host (usually the Windows PowerShell console). Because Write-Host does not go to any of the output streams, I am unable to write the results to a text file.
Output stuff
Write-Host is a useful cmdlet to precisely produce messages to the Windows PowerShell console because it does not write to an output stream. This means that I can use Write-Host to display information in an interactive fashion to a person running a Windows PowerShell script, and this information will not go on to an output file. For example, if I want to prompt a user to “press any key to begin” I can use Write-Host. Or I can use Write-Host to display status messages such as “counting services” or some other such prompt.
Of course, I can also use Read-Host and Write-Progress to perform these types of tasks. Often in Windows PowerShell, I have more than one way of doing things. One of the things you may want to read about is Windows PowerShell best practices. I have written a collection of posts where I talk about various aspects of Windows PowerShell scripting. For more information, see these Hey, Scripting Guy! Blog posts.
One hint as to what might be happening with your script is that after I run it, the console output box only contains the results from the two Write-Host cmdlets. Everything else writes to the text file.
The easy change
The easiest fix to your script is to simply change Write-Host to Write-Output. Of course, when you do that, you also need to delete the –ForegroundColor portion of your script. Now when the script runs, it will write everything to the text file and nothing to the screen.
If you need to output to both the screen and the text file, you can use the Tee-Object cmdlet. For more information about Tee-Object, read How Can I Both Save Information in a File and Display It on the Screen?
There are a number of other changes that could be made to the script to make it a bit leaner, but that is a subject for another post.
BP, that is all there is to troubleshooting script output. Join me tomorrow when I will talk about more cool Windows PowerShell stuff.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy