A Problem with the Microsoft Script Repository?

PowerShell Warning

I enjoy the Microsoft PowerShell Script Repository and all the examples provided. I have used many of the examples within PowerShell Tutorials and Articles (including this one) written on this site. However, there is a problem with using the "Write-Host" cmdlet that has frustrated myself along with many others; I have the hate-mail to prove it!

So what is the issue that is being discussed here? You will want to read on…

The issue is with the use of the "Write-Host" cmdlet in the Microsoft PowerShell Script Repository scripts. It's not that it doesn't work, it does and it works well. It's what we can't do that causes the frustration. We can't redirect the gathered data to a file. I found this out while working on the new WMI Tutorial - Part 3. Creating Reports (yes, it will be released soon).

Let's look at an example:
Copy the code into a PowerShell Script called LocalTime.ps1

 $strComputer = "."

$colItems = get-wmiobject -class "Win32_LocalTime" -namespace "root\CIMV2" `
-computername $strComputer

foreach ($objItem in $colItems) {
      write-host "Day: " $objItem.Day
      write-host "Day Of Week: " $objItem.DayOfWeek
      write-host "Hour: " $objItem.Hour
      write-host "Milliseconds: " $objItem.Milliseconds
      write-host "Minute: " $objItem.Minute
      write-host "Month: " $objItem.Month
      write-host "Quarter: " $objItem.Quarter
      write-host "Second: " $objItem.Second
      write-host "Week In Month: " $objItem.WeekInMonth
      write-host "Year: " $objItem.Year
      write-host
}

Run the script, as expected the information is outputted in the console:

.\LocalTime.ps1

Looks good, now I want to gather the same information and save the results to a file called LocalTime.txt. This is where the frustration begins, espically if you are a VBScripter (as I) and your expectation is that you can use a redirector (>) to send the info to a file. So, what happens when you fire off the following code?

.\LocalTime.ps1 > "C:\LocalTime.txt"

  • Script Runs
  • Information it output to the console
  • A new file called LocalTime.txt is created on the root of C: drive
  • No information has been redirected to the file. The file is empty

 Okay, let's attempt the same thing using the "Out-File" cmdlet, maybe that will work?

.\LocalTime.ps1 | Out-File -filepath "C:\LocalTime.txt" -append

Darn! Same result, no data exists in the file.

There is actually no issue here, the "Write-Host" cmdlet is doing what it was designed to do. It outputs the results to the console. So in reality "nothing" is being redirected to the file.

Issue:
Not able to redirect Microsoft PowerShell Script results to a file using a redirector (>) nor the "Out-File" cmdlet.

Requirements:
I require the ability to run PowerShell scripts from the MS Script Repository and choose whether to output data to the console or redirect to a file.

Resolution:
There are a number of ways to fulfill this requirement; change script to use "Write-Output" cmdlet, Save full script results to a variable, or make a small modification to the Script files provided by Microsoft. I chose to make a small modification using notepad, here is my solution:

1. Open the ListTime.ps1 script in Notepad.

PowerShell Scripting List Time

2. From the Edit menu, choose "Replace…" -or- 'Ctrl+H'

PowerShell Scripting Replace

3. We are going to remove the "Write-Host" cmdlet from the script. In Find what: text field, enter Write-Host - In Replace with: text field, leave blank. Click Replace All. The script should now look like this:

PowerShell Scripting Remove Write-Host

4. The next thing is to concatenate the string and variable. Again, open the Replace function in Notepad. This time enter the following: Findwhat: " $ (this is Double-quote - space - dollar sign). Replace with: " + $ (this is double-quote - space - plus operator - space - dollar sign). The reason we don't find $ and replace with + is it would cause a change to the variables ($strComputer, $objItem, and $colItems). Changing $objItem to +objItem results in errors. Click Replace All. You should see the following results in the script:

PowerShell Scripting Write-Host edited

Now let's see if the new script fulfills the requirement of being able to output to the console or a file.

.\LocalTime.ps1

Great the output is directed to the cosole.

How about directing results to the file C:\LocalTime.txt? Note: Since the file already exists I'm just going to append inforamtion to the file by using (>>).

.\LocalTime.ps1 >> "C:\LocalTime.txt"

Open the LocalTime.txt file. Cool the results exist in the file…

What about using?

.\LocalTime.ps1 | out-file -filepath "C:\LocalTime.txt" -append

Excellent, the data has been appended to the file.
     tip: If you want to add a break between the data just add a Null string in the script, like so:
    …
     "Week In Month: " + $objItem.WeekInMonth
     "Year: " + $objItem.Year
    " "

}

Conclusion:

Since I like to have the option of redirecting to a file, this is how I modify the PowerShell scripts found in the MS Script Repository. Most of the time I am running a script against multiple machines and this gives me a means of capturing ALL the data for analysis, it may not be practical to send gobs of info to the console buffer. If the buffer isn't large enough you wont see ALL the results. Use the techniques discussed to get past the frustration you may encounter with the "Write-Host" cmdlet, which is prevalent in the PowerShell script examples found in the Microsoft PowerShell Script Repository.

Email This Post Email This Post

These icons link to social bookmarking sites where readers can share and discover new web pages.
  • del.icio.us
  • Digg
  • Furl
  • Reddit
  • Technorati
  • YahooMyWeb

3 Comments

  1. Don Jones:

    Some background: Write-Host sends objects directly to Out-Host, which displays them on the screen. There’s no change for redirection to step in.

    Write-Output, on the other hand, writes objects to the pipeline. Normally, the pipeline ends inn Out-Default - which redirects objects to Out-Host and thus displays them on-screen. However, if you do something like

    Dir | Out-File c:\dir.txt

    The objects go from the pipeline into a file. Because most of the Out-* cmdlets don’t emit objects, the pipeline after Out-File is empty, so nothing is displayed on-screen.

    There IS a lot of misunderstanding about the difference between Write-Host and Write-Output. A simple example is:

    Write-Output 1,2,3 | Where { $_ -gt 1 }
    Write-Host 1,2,3 | Where { $_ -gt 1 }

    In the first, three objects are dumped into the pipeline. Where-Object filters out the first, leaving two to go on to Out-Default and then Out-Host. So you see 2 and 3. In the second example, three objects are written to Out-Host. There’s nothing in the pipeline, so Where-Object has nothing to filter. Nothing goes to Out-Default. So you see 1, 2, and 3 - because they bypassed the pipeline and went directly to the screen.

    About the only way to “redirect” Write-Host is to use Start-Transcript and Stop-Transcript, since they explicitly grab everything that appears on-screen, no matter how it gets there.

  2. Jesse Hamrick:

    Don,
    Thanks for providing more information on once the Write-Host cmdlet ouputs to the screen, there is no data to enter the pipeline.
    Just want to tweak your examples as I’m getting a datatype error with example 1.

    Example 1.

    Write-Output (1, 2, 3) | Where {$_ -gt 1}

    Example 2.

    Write-Host (1, 2, 3) | Where {$_ -gt 1}
  3. Eric:

    Of course, using Write-Output doesn’t allow colors, so one of the attractive features of PowerShell is lost (I’d ~like~ to use it to call attention to certain results, but now I can’t since I also want to redirect results to a file).

    Compound this with Functions’ behavior of “returning” all output as a result unless it’s sent to Write-Host, and we get into an overly difficult game of Twister….

Leave a comment