A Problem with the Microsoft Script Repository?

By Jesse Hamrick • March 5th, 2008

PowerShell WarningI 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

Find and 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

Write-Host Removed

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

Concatenate

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 To A Friend Email This Post To A Friend

Comments

By Don Jones on March 7th, 2008 at 11:09 am

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.

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}

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….

how do i obtain the redirected drive using powershell scripts

Here is a nice :) touch to the above.
Place your script into a function, let’s call it TIME. Create a simple Menu that depending on the response executes the function:

Press 1 <– To output the results to the console/screen
Press 2 <– To output the results to a file
$Response = Read-Host “Please make your selection ”

If ($Response -eq 1) {TIME}
else {TIME | out-file -filepath “C:\LocalTime.txt” -append}

#You are Done!

Try this script. If you run this within PowerGUI and step through each line of code as it runs, you’ll find that the “write-output” and the return value of 2 all get stuffed into the variable $result. So, $result really is assigned a system.object[] value, that contains two things (e.g. $result[0] will be the text string, and $result[1] will be the number 2.

# —–
function test() {Write-Output “A line of text.”;return 2}
Write-Output “Begin”
$result = test
Write-Output “End, $result”
# —–

Thanks for this. This problem had me foxed for ages until I read your simple explanation!

Thanks very much for this tip!

This really helped me out.

Hello;

I have a simple question about sharepoint/powershell.

I am enumerating a list and I display the items from a column.

If I use this command:
write-host $listItem["Activity"]

The powershell console will print:
string;#MyActivityName

Why is powershell appending string;# to my output?

I realize its probably easy to trim it off but I am curious why it appends in the first place?

Thanks. Nice work-around!

Use the function below and switch as you move from environment to another.
function Write($Message)
{
#Write-Host $Message
Write-Output $Message
}

Grt Help! Thanks Don

 

Leave a Comment

« | Home | »