Tag Archives: Write-Output

Script Sharing – Write-Output Gets Foreground and Background Colors and More

Every once in a while a forum post comes along that really captures my interest. Take this one for instance.

In this post, the creator wanted to build out a start up menu in the ConsoleHost that has a solid white line, another white line below that, with the words “PowerShell Project 1” in it, and a final white line beneath that. That might be difficult to imagine, so here’s a visual representation, and then what his/hers looked like. I had recommended to use a here-string to fix the problem, but unfortunately we didn’t have the same results.

write-output-gets-foreground-and-background-colors-and-more01
Note: While you can’t see them, in both this and the below image, there are spaces on the lines above, below, and before and after “PowerShell Project 1.” This is how we can ensure the white rectangle around the text.

write-output-gets-foreground-and-background-colors-and-more02

I can’t say for certain why theirs rendered differently, but I suspect version, or PSReadline, or possibly console font/size. That wasn’t important; what was, was trying to figure out a way around this problematic inconsistency. We have the option to change the background color of the area where we type, and so I wondered if I could temporarily do that inside the ConsoleHost. It turns out I could. With that new piece of knowledge, I set out to write a wrapper function around Write-Output. In my version — Write-TMOutput — it includes -ForegroundColor and -BackgroundColor parameters with an option to horizontally and vertically pad the text (object).

First, here’s a few images to show my new function in action. This first image shows the commands included in the TMOutput module.

write-output-gets-foreground-and-background-colors-and-more03

The next example image shows some basic, Write-Output type usage. One where we pipe to the function, and one where we don’t. Fairly standard.

write-output-gets-foreground-and-background-colors-and-more04

Now, the next example is where this starts to get fun: the incorporation of the -ForegroundColor and -BackgroundColor parameters. Prior to running these commands, and those in the last two examples, I removed the PSReadLine module so the colors would be easier to spot.

write-output-gets-foreground-and-background-colors-and-more05

Next is a demo of two additional parameters: -HorizontalPad and -VerticalPad. These will allow you to add spaces before and after the value supplied to the -InputObject parameter (the text), and add lines above and below the text. This is the part that gets back to the request in the Microsoft Technet forum post. I should mention that my solution in the forum post would be different now that I’ve spend some time writing this function — there are better ways to do things that I hadn’t considered at the time I replied to the post.

write-output-gets-foreground-and-background-colors-and-more06

The next example shows some usage with the Get-ADUser cmdlet. Keep in mind that if you combine this function with other cmdlets, that the -HorizontalPad and -VerticalPad parameters cannot be used. In my mind, they’re just a bonus to the option of using colors with Write-Output. I should mention it now, but this function was intentionally written to only work in the ConsoleHost. Perhaps I’ll add the option of using it in the ISE in the future.

write-output-gets-foreground-and-background-colors-and-more07

I’ve written an additional function that will allow you to quickly see the available colors from which you can choose. It’s called Show-TMOutputColor, and produces the results below. This might come in handy as you’re using the modified Write-Output.

write-output-gets-foreground-and-background-colors-and-more08

Now, on to the module. It’s the first time I’ve done it, but I’ve uploaded this to the PowerShell Gallery. I believe publishing to the PowerShell Gallery was one of my PowerShell New Year’s Resolutions for 2016, so there’s one down! You can use this direct link or, one better, download the module from your console, using this command:

Install-Module -Name TMOutput

Thanks for reading this post. Hopefully these function will be a useful way to get around the need for Write-Host’s foreground and background color options while working in the console. That, and maybe the TechNet thread creator can do whatever they wanted, too.

Script Sharing – Multi-Level Menu System with a Back Option

It’s been said several times now, but while this site was designed to help people learn some Windows PowerShell, it’s also about posting things I’m going to want to find one day. Take for instance this menu system I started for someone on TechNet: https://social.technet.microsoft.com/Forums/en-US/30663446-4091-4a1c-9de0-407046ccc39f/powershell-script-with-submenus-how-to-go-back?forum=winserverpowershell.

It allows the user the ability to enter into menus and submenus with the option of backing out of them. You know, choose a number from the menu, or hit B to go back to the previous menu. Hopefully it’s helpful for the TechNet thread creator, and maybe it’s helpful for others someday, too. I’ve included both the code and an image of the code in action. Until next time.

Do {
@'

----------Software/Driver Installation----------
1. Toshiba 1
2. Acer 1
------------------------------------------------

'@

    $MainMenu = Read-Host -Prompt 'Enter 1 - 2 or Q to quit'
    Switch ($MainMenu) {
        1 {
            Do {
@'

---------Software/Driver Installation----------
1. Software
2. Drivers
------------------------------------------------

'@
                $1MainMenu = Read-Host -Prompt 'Enter 1 - 2 or B for Back'
                Switch ($1MainMenu) {
                    '1' {
                            Do {
@'

--------------Software Installation-------------
1. Package 1
2. Package 2
3. Package 3
------------------------------------------------

'@
                                $1InnerMenu = Read-Host -Prompt 'Enter 1 - 3 or B for Back'
                                Switch ($1InnerMenu) {
                                 '1' {Write-Output -InputObject '--> You chose to install package 1'; break}
                                 '2' {Write-Output -InputObject '--> You chose to install package 2'; break}
                                 '3' {Write-Output -InputObject '--> You chose to install package 3'}
                                }
                            } Until ($1InnerMenu -eq 'B')
                        }
                }
            } Until ($1MainMenu -eq 'B')
        }
    } # End Switch.
} Until (
    $MainMenu -eq 'Q'
)

multi-level-nested-menu-system-with-a-back-option01

Quick Learn – PowerShell Nested Switch Statements

There was a Reddit post recently that gave me an opportunity to try something I’m not sure I’ve tried before: nested switch statements — switch statements inside switch statements. So we’re on the same page, I’ve written an example of a basic, non-nested switch. It provides some of the same functionality as an If-ElseIf. I’ll typically use a switch statement instead of an If-ElseIf when there’s three or more outcomes. It’s easier to read, and it looks cleaner.

In the example below, a specific phrase will be written if the value in the $Fruit variable is apple, banana, or pear. If it’s not one of those, it’ll take the default action. In the case below, the default action indicates that an apple, banana, or pear wasn’t chosen. Switch statements will sometimes be called case statements in other languages.

$Fruit = 'banana'
Switch ($Fruit) {
    'apple' {'You chose apple.'; break}
    'banana' {'You chose banana.'; break}
    'pear' {'You chose pear.'; break}
    default {'You didn''t choose an apple, banana, or pear.'}
}

As a part of my nested switch statement testing, I wrote this example using automobile makes and models. The outermost switch statement makes a determination of which nested switch statement to execute.

$Make = 'Ford'
$Model = 'Escape'
 
Switch ($Make) {
    'Ford' {
        Switch ($Model) {
            'Mustang' {Write-Output -InputObject 'You selected the Ford Mustang.'; break}
            'Escape' {Write-Output -InputObject 'You selected the Ford Escape.'; break}
            default {Write-Output -InputObject "You selected Ford, however, the $Model isn't a valid model for this make."; break}
        }
    }
    'Chevy' {
        Switch ($Model) {
            'Corvette' {Write-Output -InputObject 'You selected the Chevy Corvette.'; break}
            'Camaro' {Write-Output -InputObject 'You selected the Chevy Camaro.'; break}
            default {Write-Output -InputObject "You selected Chevy, however, the $Model isn't a valid model for this make."; break}
        }
    }
    'Dodge' {
        Switch ($Model) {
            'Charger' {Write-Output -InputObject 'You selected the Dodge Charger.'; break}
            'Viper' {Write-Output -InputObject 'You selected the Dodge Viper.'; break}
            default {Write-Output -InputObject "You selected Dodge, however, the $Model isn't a valid model for this make."; break}
        }
    }
}

Should we take this further — a switch statement inside a switch statement inside a switch statement? I’m not going to lie, it started to get confusing and so my first recommendation would be to try and steer clear of the multi-nesting this deep (3 levels). I’ve removed some of the example above — Chevy and Dodge — in my example below and focused on the Ford (pun, absolutely intended). To help, I’ve included comments on the closing brackets. This multi-nested switch statement will return different values based on the make, the model, and the year, too.

$Make = 'Ford'
$Model = 'Escape'
$Year = '2016'
  
Switch ($Make) {
    'Ford' {
        Switch ($Model) {
            'Mustang' {
                Switch ($Year) {
                    '2015' {Write-Output -InputObject "You selected the 2015 Ford Mustang."; break}
                    '2016' {Write-Output -InputObject "You selected the 2016 Ford Mustang"; break}
                    default {Write-Output -InputObject "You selected the Ford Mustang with a non-matching year."; break}
                } # End Mustang Year Switch.
            } # End Mustang Switch.
            'Escape' {
                Switch ($Year) {
                    '2015' {Write-Output -InputObject "You selected the 2015 Ford Escape."; break}
                    '2016' {Write-Output -InputObject "You selected the 2016 Ford Escape."; break}
                    default {Write-Output -InputObject "You selected the Ford Escape with a non-matching year."; break}
                } # End Escape Year Switch.
            } # End Escape Switch.
            default {Write-Output -InputObject "You selected a Ford; however, the $Model isn't a valid model for this make."}
        } # End Ford Model Switch.
    } # End Ford Switch.
    default {Write-Output -InputObject 'Sorry, we''re only dealing with Fords (at the moment).'}
} # End Make Switch.

So I can track it down later, here’s the post I read on Reddit that influenced this post. It includes the link to my possible, partial solution; however, that direct link is here: http://pastebin.com/KRxRb1VM.

Update: It has occurred to me that it might be easier to follow this thrice nested switch without all the comments. I’ve removed those below.

$Make = 'Ford'
$Model = 'Escape'
$Year = '2016'
  
Switch ($Make) {
    'Ford' {
        Switch ($Model) {
            'Mustang' {
                Switch ($Year) {
                    '2015' {Write-Output -InputObject "You selected the 2015 Ford Mustang."; break}
                    '2016' {Write-Output -InputObject "You selected the 2016 Ford Mustang"; break}
                    default {Write-Output -InputObject "You selected the Ford Mustang with a non-matching year."; break}
                }
            }
            'Escape' {
                Switch ($Year) {
                    '2015' {Write-Output -InputObject "You selected the 2015 Ford Escape."; break}
                    '2016' {Write-Output -InputObject "You selected the 2016 Ford Escape."; break}
                    default {Write-Output -InputObject "You selected the Ford Escape with a non-matching year."; break}
                }
            }
            default {Write-Output -InputObject "You selected a Ford; however, the $Model isn't a valid model for this make."}
        }
    }
    default {Write-Output -InputObject 'Sorry, we''re only dealing with Fords (at the moment).'}
}

Quick Learn – Using the Range Operator for Calculating Total Push-Ups

In December of 2014, I decided that my life in 2015 was in need of some push-ups. Instead of just starting with 10 a day, or some other arbitrary number, I thought I would do as many push-ups a day as it was the day in the year. This meant that on day one (January 1, 2015), I would do one push-up and on day two, I would do two push-ups, and so on. Today is the 20th day of the new year, and so I’ll have to do 20 tonight. I wanted to know how many push-ups I will have done by January 31st. Being the Windows PowerShell hobbyist that I am, I enlisted PowerShell to do my calculations for me.

I started with a variable, $EndDay, and the range operator (..). The combination of the two provides me an integer array of the days in January, such as 1..$EndDay (or, 1..31). Using this, I can calculate how many total push-ups I will have done by the end of the day on January 31st. The example below sets up the integer array, as well as the ForEach-Object loop where we’ll do our calculations. Note: I’m using the ForEach-Object alias, foreach.

$EndDay = 31
1..$EndDay | foreach {

}

The first thing we do, below, is include a second variable, $PushUps, that will collect the total number of push-ups for the month. We’ll use the += assignment operator. This operator takes whatever is already in $PushUps, and adds to it. If the current value stored in $PushUps was 1, and we used the += assignment operator like so, $PushUps += 2, then the value in $PushUps would be 3 (1 + 2 is equal to 3). If we used the standard assignment operator (=), then $PushUps would be 2, as 1 would be overwritten.

On the next line, below, we write some information on the screen. We write the current day: that’s the current number from the integer array represented by $_ (as of PowerShell 3.0, $_ can be represented as $PSItem). Then we write out the total number of push-ups completed by that day: $PushUps.

$EndDay = 31
1..$EndDay | foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
}

I noticed that when I reran the code in the ISE, that the value of $PushUps was incorrect on the second run. This is because the variable already exists, and by the end of the first run already contains 496—the number of push-ups I’ll have done by the end of January! Therefore, I added an If statement that removed the $PushUps variable when $_ was equal to $EndDay. This happens on the final run through the foreach.

$EndDay = 31
1..$EndDay| foreach {
    $PushUps += $_
    Write-Output -Verbose "Day: $_ / PushUp Total: $PushUps"
    If ($_ -eq $EndDay) {
        Remove-Variable PushUps
    }
}

If you change the value for $EndDay to 365, you’ll be able to determine that after December 31st (if I can somehow keep this up) I will have done 66,299 total push-ups for the year. It’s hard to imagine that I could do 365 push-ups at once, but then again, it’s hard to imagine I’ll get though the rest of the month. Here’s an image that shows the the full results when we run the function above.

Using the Range Operator for Push-Up Calculations

Thanks for reading, and wish me good luck—I’m going to need it.

Quick Learn – Write-Host, Does it Have a Place?

I’ve been working on an advanced function that I can’t wait to share (and no, it’s not the one in this post). I really think it’s something that the Windows PowerShell community has been missing. Okay fine, maybe it’s just something I’ve been missing.

I noticed in development that my Write-Output messages to the user were crossing the pipeline, when the custom object (created by the function), was passed to Select-Object (in certain manners). I have a function below that does the same thing as the one in development.

Here’s how this thing works: The function requires the user to provide the value Write-Output (or, wo), or Write-Host (or, wh) for the -Option parameter. This will determine how the message to the user is written inside the Begin block. The only other thing that’s happening in this function, is that a custom object is being created in the Process block, based on some properties of Win32_BIOS.

Function Test-TMWriteOutputVsHost {
    [CmdletBinding()]
    Param (
    [Parameter(Mandatory=$true,
        HelpMessage="Enter Write-Output or Write-Host")]
    [ValidateSet('Write-Output','wo','Write-Host','wh')]
    [string]$Option
    )

    Begin {
        If ($Option -eq 'Write-Output' -or $Option -eq 'wo') {
            Write-Output 'Inside the Begin block (using Write-Output).'

        } ElseIf ($Option -eq 'Write-Host' -or $Option -eq 'wh') {
            Write-Host 'Inside the Begin block (using Write-Host).'
        }
    } # End Begin

    Process {
        $CollectionVariable = Get-WmiObject -Class Win32_BIOS
        $Object = @()
        $Object += [pscustomobject]@{
            Manufacturer = $CollectionVariable.Manufacturer;
            Name = $CollectionVariable.Name;
            Version = $CollectionVariable.Version
        }
    } # End Process

    End {
        Write-Output $Object
    }
    # End, End
} # End Function

As we can see below, everything works great with both Write-Output or Write-Host, when we don’t pipe the function to the Select-Object cmdlet.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output
Inside the Begin block (using Write-Output).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

Now, let’s pipe our object to some variations of the Select-Object cmdlet and watch some things blow up (when using Write-Output).

PS C:\> # Use the horizontal scrollbar to see the results...
PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select *

                                                                                                                 Length
                                                                                                                 ------
                                                                                                                     44

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | select Name,Ver*

Name                                                        Ver*
----                                                        ----

BIOS Date: 08/27/13 11:12:44 Ver: A13.00

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select *
Inside the Begin block (using Write-Host).

Manufacturer                            Name                                    Version
------------                            ----                                    -------
Dell Inc.                               BIOS Date: 08/27/13 11:12:44 Ver: A1... DELL   - 1072009

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | select Name,Ver*
Inside the Begin block (using Write-Host).

Name                                                        Version
----                                                        -------
BIOS Date: 08/27/13 11:12:44 Ver: A13.00                    DELL   - 1072009

The problem with Write-Output, is that the object it’s producing is crossing our pipeline and causing unpredictable behavior—something we can’t include in a function we want to distribute. Here’s the proof: When we pipe our function to Get-Member, we reveal the objects that show up on the other side, and we don’t want the string object coming over with us.

PS C:\> Test-TMWriteOutputVsHost -Option Write-Output | Get-Member | Select-Object TypeName -Unique

TypeName
--------
System.String
System.Management.Automation.PSCustomObject

PS C:\> Test-TMWriteOutputVsHost -Option Write-Host | Get-Member | Select-Object TypeName -Unique
Inside the Begin block (using Write-Host).

TypeName
--------
System.Management.Automation.PSCustomObject

I don’t profess to know it all, so if there’s a way to get around this using Write-Output, then I’d love to hear about it. While I haven’t tried it, I suspect I may be able to create an embedded function—that might be the trick I need. Perhaps I’ll play with that option at another time. Thanks for reading!

Oh, and before someone mentions it, I explained how I feel about Write-Verbose in the comments on a post by Adam Bertram: http://www.adamtheautomator.com/use-write-host-lot.