Friday, May 16, 2008 7:19:58 AM UTC #

I am sometimes asked what PowerShell is. While it's easy enough to give a technical overview, today I'll focus on everything else.

PowerShell is not a fly-by-night scripting tool

It isn't JScript, and won't disappear anytime soon. For the three JScript enthusiasts remaining in the world, I apologize for the cheap shot. Wait, no I don't. It's JScript—no one's using it, no one will be offended.

PowerShell has had a rapid rise since it reached beta status in late 2006, and if you're wondering if this positive trend will continue, let me lay it out for you eloquently:

WHY YES IT WILL, AS A MATTER OF FACT

You may have already heard that Exchange 2007 uses PowerShell for its administration console. Which is great, but I personally couldn't care less—I've got bigger fish to fry.

SQL 2008: PowerShell support. Still don't care, but we're getting warmer.

IIS 7: PowerShell provider. Ok, I like this. Still in beta though, we'll give it some time before any "visible enthusiasm."

administrative duties for Microsoft products going forward - pretend the link to the left is from an authoritative source. Because had I better searching skills, it would be. The point is: major Microsoft server products will begin to support PowerShell in the same way as Exchange 2007 (which again, we couldn't care less about).

If all that hasn't convinced you, maybe the visual aid below will:

image 

In Windows Server 2008, PowerShell is an available Feature! We're beyond installers; PowerShell is now built into the operating system! This is even true of the older Operating Systems—if you don't believe me, try uninstalling PowerShell! Can't find it? That's because (in the older OSes anyway) it's installed as an update to Windows, as a standalone hotfix. Hotfix, hotness! If I was rapping I could make "hotfix" and "hotness" rhyme. I could  pull it off.

PowerShell is not going to replace the command shell (cmd.exe)

I briefly thought this would be the case, then Server 2008 Core dashed my hopes. While I admire the engineering-won-over-marketing victory that allowed Server Core to ship with almost absolutely nothing, including all of the .NET framework, I wish we could have simply declared 2008 as "year of the PowerShell" and Server 2008 as "the painful version for the PowerShell laggards." That would have been awesome.

But, as it turns out, Server Core did indeed ship without PowerShell, and as such, we still have cmd.exe available to us, awful parsing rules and all (e.g. try the following: echo "greater than sign is >" - I assure you that will not produce the desired effect). Anyway.

You are not wasting your time learning PowerShell

First, I'll point out that PowerShell is actually quite easy to pick up, and that you can do so with a single PowerShell book. Furthermore, I'll point out that the currently-in-pre-alpha-V2 version of PowerShell is additive and does not contain any breaking changes. Like C#, PowerShell will grow, only. And grow, awesome!

PowerShell doesn't have to be slow

I've read that the PowerShell process will be slow loading the first few times. Which is terrible, because many of us won't make it past those first few times, especially those of us who are cynical of the whole thing to begin with. I assure you, it gets faster.

PowerShell is useful

I'm not telling you to pick up PowerShell to satisfy your yearly programming language learning quota. I'm also not telling you PowerShell will change the way you code in Java or COBOL or SNOBOL or FORTRAN or RPG or whatever. I will instead say that I have saved myself time (and more importantly, aggravation) with PowerShell, whether by spelunking the SharePoint object model, completely automating tasks, or running scripts as post-build actions on my Visual Studio projects…

PowerShell saves me time. And if it doesn't save me time, then it's saving my sanity. And if it isn't doing either of those two things, then it's probably pulling in some mixture of .NET framework classes, using enhanced filesystem/XML/registry support, AD stuff, COM objects, and yes, even running other console applications—it's pulling any or all of these things into one environment, in a way you won't see anywhere else. PowerShell is the de-facto scripting environment for Windows—in fact I used to call it "Perl for Windows, except Perl for Windows already exists, and actually competes directly with PowerShell." Yes, I can't help myself when describing PowerShell and say the quote in full, all the way to the end of the long rambling sentence (like this one (it doesn't seem to ever end)). Well, let me set the record straight as to PowerShell and Perl:

PowerShell is better than Perl

There, I said it.

Well, for Windows, anyway.

Categories: PowerShell
Technorati:
Friday, May 16, 2008 7:19:58 AM UTC  #     |  Comments [0]  |  Trackback
Wednesday, April 23, 2008 10:56:08 AM UTC #

Update: welcome to the new-and-improved recap! This recap, unlike the last, weighs in at less than a million billion byes of text and code highlighting and will now load in your feed reader.

This is a recap of my entries into the 2008 Winter Scripting games. From, like, the winter. I know, it's April, we're already in summertime mode here in Texas, and it is quite clear that it is no longer 'the winter'. Let's move on, shall we?

I'm going to work through each of the following scripts illustrated in the table below (the counts for "Lines", "Words", and "Characters" mean what you would imagine they mean):

image

Compare the above table to MOW, who, for example, had a 1 line, 8 word, 58 character solution for #6. My solutions are (relatively) HUGE. So my recaps, which contain the full source, will necessarily be HUGE as well.

For each problem, I'm going to quickly sum up the interesting (at least TO ME) bits of each problem, then I'm going to post the full source.

2008 Winter Scripting Game Events: Index

Categories: Awesomeness | PowerShell
Technorati:  | 
Wednesday, April 23, 2008 10:56:08 AM UTC  #     |  Comments [0]  |  Trackback
Wednesday, April 23, 2008 3:47:17 AM UTC #

If you aren't already in the know, these ten problems are from the 2008 Winter Scripting games.

For each problem, I'm going to quickly sum up the interesting (interesting TO ME) bits of each problem, then I'm going to post the full source.

Event 10: Blackjack!

Here we are asked to simulate a text-based Blackjack game. Mine works, I'm certainly not proud of the graphics, not proud of the colors, not even necessarily proud of the structure of my solution.

Please do not look at any of the following code and think "that's how I'm supposed to use objects in PowerShell!" Please don't. It's NOT ideal, I made several design mistakes; I don't even think most of my objects are necessary. Or, thinking from the other side—maybe I didn't go far enough.

Source

#PROBLEM #10
$cardValues = @{
    "Ace" = 11;
    "King" = 10;
    "Queen" = 10;
    "Jack" = 10;
    "Ten" = 10;
    "Nine" = 9;
    "Eight" = 8;
    "Seven" = 7;
    "Six" = 6;
    "Five" = 5;
    "Four" = 4;
    "Three" = 3;
    "Two" = 2;
}


$suits = @("Spades", "Hearts", "Diamonds", "Clubs")


#bad random number generator
$rnd = new-object Random




function Create-DeckObject
{
    $o = new-object PSObject
    $cards = @()
    foreach ($suit in $suits)
    {
        foreach ($cardValue in $cardValues.Keys)
        {
            $cards += Create-CardObject -value $cardValue -suit $suit
        }
    }
   
    $sortedDeck = $cards | sort PositionInDeck
   
    Add-Member -inputObject $o -memberType NoteProperty -name "Cards" -value $sortedDeck
    Add-Member -inputObject $o -memberType ScriptProperty -name "Deal" -value { $nextCard = $this.Cards | select -first 1; $this.Cards = $this.Cards | select -last ($this.Cards.Count - 1); $nextCard }
   
    $o
}


function Create-CardObject ($value, $suit)
{
    $o = new-object PSObject


    Add-Member -inputObject $o -memberType NoteProperty -name "Card" -value ("$value of $suit")
    Add-Member -inputObject $o -memberType NoteProperty -name "BlackjackValue" -value $cardValues[$value]
    Add-Member -inputObject $o -memberType NoteProperty -name "PositionInDeck" -value $rnd.NextDouble()
   
    $o
}


function Create-HandObject ([switch]$isdealer)
{
    $o = new-object PSObject


    $emptyHand = @()
    Add-Member -inputObject $o -memberType NoteProperty -name "Cards" -value $emptyHand
    Add-Member -inputObject $o -memberType NoteProperty -name "IsDealerHand" -value $isdealer


    #this value will change for the dealer hand as the game progresses.
    Add-Member -inputObject $o -memberType NoteProperty -name "AreAllCardsRevealed" -value (-not $isdealer)
    Add-Member -inputObject $o -memberType ScriptProperty -name "BlackjackValue" -value { $total = 0; $this.Cards | % {$total += $_.BlackjackValue}; $total }
   
    $o
}


function Print-VisibleCards ($cards,$allcardsvisible,[switch]$isdealer)
{
    $output = @()
    if ($allcardsvisible -eq $TRUE)
    {
        foreach ($card in $cards)
        {
            $output += $card.Card
        }


        #POSTSCRIPT-OOPS! Looks like I'm calculating the "total value of hand", when
        #I expose that value as a script property! This function would have been
        #IDEAL as a ScriptMethod attached to the Hand object.
        $total = 0; $cards | % { $total += $_.BlackjackValue }
        if (-not $isdealer)
        {
            $output += "You have $($total).`n"
        }
        else
        {
            $output += "Dealer has $($total).`n"
        }
    }
    else
    {
        #if the dealer's hand is invisible, we don't know their total, so
        #don't display it; also we can only see the second card.
        $output += $cards[1].Card
    }
   
    $output | out-string
}


function Show-Status ($my, $dealer)
{
    Write-Host "Your cards:"
    Write-Host (Print-VisibleCards -cards $my.Cards -allcardsvisible $TRUE) -foregroundColor Green
   
    Write-Host "Dealer's cards:"
    Write-Host (Print-VisibleCards -cards $dealer.Cards -isdealer -allcardsvisible $dealer.AreAllCardsRevealed ) -foregroundColor Red
}


function Show-Action ($text)
{
    Write-Host $text -foregroundColor Yellow
    Start-Sleep -seconds 1
}


function Solve-Problem10
{
    $deck = Create-DeckObject
   
    $myHand = Create-HandObject
    $dealerHand = Create-HandObject -isdealer
   
    Show-Action "Dealing starting hands…"
    $myHand.Cards += $deck.Deal
    $dealerHand.Cards += $deck.Deal
    $myHand.Cards += $deck.Deal
    $dealerHand.Cards += $deck.Deal


    #show starting hands
    Show-Status -my $myHand -dealer $dealerHand
   
   
    #give the player the option to hit
    while ($myHand.BlackjackValue -lt 21)
    {
        Write-Host -noNewLine "Stay (s) or Hit (h) ? "
        if ((Read-Host) -match "h")
        {
            Show-Action "You have chosen to Hit."
            $myHand.Cards += $deck.Deal
            Show-Status -my $myHand -dealer $dealerHand
        }
        else
        {
            Show-Action "You have chosen to Stay."
            break
        }
    }
   
    #did we bust? If so, quit.
    if ($myHand.BlackjackValue -gt 21)
    {
        Show-Action "YOU LOSE! You busted!"
        return
    }


    #dealer reveals their cards
    $dealerHand.AreAllCardsRevealed = $TRUE
    Show-Action "Dealer has revealed their cards."


    Show-Status -my $myHand -dealer $dealerHand
    #extra delay so that we can process what is in the dealer's hand.
    Start-Sleep -seconds 3
   
   
    #give the dealer the option to hit
    while ($dealerHand.BlackjackValue -lt $myHand.BlackjackValue)
    {
        Show-Action "Dealer takes a card."
        $dealerHand.Cards += $deck.Deal
        Show-Status -my $myHand -dealer $dealerHand
    }
   
    #did dealer bust? If so, VICTORY!
    if ($dealerHand.BlackjackValue -gt 21)
    {
        Show-Action "YOU WIN! VICTORY! Dealer busts!"
    }
    else
    {
        Show-Action "YOU LOSE! Dealer has $($dealerHand.BlackjackValue), you have $($myHand.BlackjackValue)."
    }
}


Solve-Problem10

2008 Winter Scripting Game Events: Index

Categories: PowerShell
Technorati:
Wednesday, April 23, 2008 3:47:17 AM UTC  #     |  Comments [0]  |  Trackback
Wednesday, April 23, 2008 3:46:56 AM UTC #

If you aren't already in the know, these ten problems are from the 2008 Winter Scripting games.

For each problem, I'm going to quickly sum up the interesting (interesting TO ME) bits of each problem, then I'm going to post the full source.

Event 9: You're Twisting My Words

Simple: reverse letters in each word, WITHOUT reversing the order of the words. It's a simple case of breaking down the huge string into tokens, then reversing the contents of the tokens, then re-combining the tokens. Actually it's even simpler than I described. Moving on.

Source

#PROBLEM #9
function Reverse-LettersInWord ($word)
{
    $reversedChars = @()
    for ($i = $word.Length - 1; $i -ge 0; $i)
    {
        $reversedChars += $word[$i]
    }
   
    [string]::Join("", $reversedChars)
}


function Solve-Problem9
{
    [string]::Join(" ", ( ( (cat "C:\Scripts\alice.txt").Split(" ") ) | % { Reverse-LettersInWord -word $_ } ) )
}



Solve-Problem9

2008 Winter Scripting Game Events: Index

Categories: PowerShell
Technorati:
Wednesday, April 23, 2008 3:46:56 AM UTC  #     |  Comments [0]  |  Trackback
Wednesday, April 23, 2008 3:46:35 AM UTC #

If you aren't already in the know, these ten problems are from the 2008 Winter Scripting games.

For each problem, I'm going to quickly sum up the interesting (interesting TO ME) bits of each problem, then I'm going to post the full source.

Event 8: Making Beautiful Music

In this problem, we are asked to generate a random playlist for our CDs. I'll just say: I cheated. If you look carefully, you'll notice I sorted the songs from longest to shortest, making the "song packing" algorithm more likely to succeed. But it's not perfect and can fail.

So, in summary, I solved the problem like everyone else, but probably not well enough.

Source

#PROBLEM #8
$filename = "C:\Scripts\songlist.csv"
$global:nextId = 0
$minimumSecondsPerCd = (75 * 60)
$maximumSecondsPerCd = (80 * 60)
$maximumSongsPerArtistOnPlaylist = 2


function Create-SongObject ($artist, $title, $durationString)
{
    $o = new-object PSObject


    Add-Member -inputObject $o -memberType NoteProperty -name "Artist" -value $artist
    Add-Member -inputObject $o -memberType NoteProperty -name "Title" -value $title
    Add-Member -inputObject $o -memberType NoteProperty -name "Duration" -value $durationString


    Add-Member -inputObject $o -memberType ScriptProperty -name "GetSeconds" -value { Get-DurationInSeconds $this.Duration }


    Add-Member -inputObject $o -memberType NoteProperty -name "Id" -value $nextId
    $global:nextId++


    Add-Member -inputObject $o -memberType ScriptProperty -name "PrintDetails" -value { "$($this.Artist)`t$($this.Title)`t$($this.Duration)"}
   
    $o
}


#POSTSCRIPT-OOPS: I forgot the TimeSpan class! Argh! Instead of
#using TimeSpan, I implement it again here below!
function Get-DurationInSeconds ($durationString)
{
    $column = $durationString.Split(":")
    $minutes = [int]$column[0]
    $seconds = [int]$column[1]
   
    $minutes * 60 + $seconds
}


function Get-AvailableSongs
{
    foreach ($line in (cat $filename) )
    {
        $column = $line.Split(",")
        Create-SongObject -artist $column[0] -title $column[1] -durationString $column[2]
    }
}


function Sum-PlaylistTime ($playlist)
{
    #initialize to 0 in case of empty playlist
    $totalSeconds = 0
   
    foreach ($song in $playlist)
    {
        $totalSeconds += $song.GetSeconds
    }
   
    $totalSeconds
}


#POSTSCRIPT-OOPS: ok, well, this isn't an oops moment
#necessarily. But I do apologize for the THIRTY ONE
#CHARACTER variable name used below. 31!
function Has-PlaylistReachedArtistSongLimit ($artist, $playlist)
{
    $playlistArtistSongCount = $playlist | group Artist | ? { $_.Name -eq $artist } | % { $_.Count }
   
    $maximumSongsPerArtistOnPlaylist -le $playlistArtistSongCount
}


function Add-NextEligibleSong ($catalog, $existingPlaylist)
{
    $existingPlaylistIds = $existingPlaylist | % { $_.Id }
   
    foreach ($candidateSong in $catalog)
    {
        if ($existingPlaylistIds -contains $candidateSong.Id)
        {
            continue
        }
       
        if ( Has-PlaylistReachedArtistSongLimit -artist $candidateSong.Artist -playlist $existingPlaylist )
        {
            continue
        }
       
        #I should handle the possibility of building a playlist in a combinatorial manner, i.e.
        #I should be attempting to remove songs to make room for others if necessary.
        #this would be the spot for it (would require some restructuring in how I add/remove songs from the list)
        if ( ((Sum-PlaylistTime $existingPlaylist) + $candidateSong.Seconds) -gt $maximumSecondsPerCd)
        {
            continue
        }


        return $candidateSong
    }
   
    Throw "No candidate songs remain."
}


function Solve-Problem8
{
    $songs = Get-AvailableSongs | sort GetSeconds -desc
   
    $myPlaylist = @()
   
    while ((Sum-PlaylistTime $myPlaylist) -lt $minimumSecondsPerCd )
    {
        $myPlaylist += Add-NextEligibleSong -catalog $songs -existingPlaylist $myPlaylist
    }
   
    foreach ($song in $myPlaylist)
    {
        Write-Host $song.PrintDetails
    }
   
    $totalSeconds = Sum-PlaylistTime $myPlaylist
    $totalMusicMinutes = [int]([Math]::Floor($totalSeconds / 60))
    $totalRemainingSeconds = $totalSeconds % 60
    $totalMusicTime = "$($totalMusicMinutes):$($totalRemainingSeconds)"
   
    Write-Host "`nTotal music time: $totalMusicTime"
}




Solve-Problem8

2008 Winter Scripting Game Events: Index

Categories: PowerShell
Technorati:
Wednesday, April 23, 2008 3:46:35 AM UTC  #     |  Comments [0]  |  Trackback
Wednesday, April 23, 2008 3:46:13 AM UTC #

If you aren't already in the know, these ten problems are from the 2008 Winter Scripting games.

For each problem, I'm going to quickly sum up the interesting (interesting TO ME) bits of each problem, then I'm going to post the full source.

Event 7: Play Ball!

Here we are asked to randomly schedule games for six teams in a round robin tournament. Apparently I used Knuth's classic list order randomization algorithm, which I remember attempting to implement, badly, and failing!,  in high school. Yay for progress.

Otherwise the solution is straightforward: two nested loops, generate matches in a "combinatorial" way, then randomize the order. Boom, done.

Source

#PROBLEM #7
$teams = @("A", "B", "C", "D", "E", "F")


#bad random number generator
$rnd = new-object Random


function Create-GameObject ($game)
{
    $o = new-object PSObject
    Add-Member -inputObject $o -memberType NoteProperty -name "Game" -value $game
   
    Add-Member -inputObject $o -memberType NoteProperty -name "RandomOrder" -value $rnd.NextDouble()   
   
    $o
}


function Generate-RoundRobinGames
{
    for ($firstTeamIndex = 0; $firstTeamIndex -lt $teams.Count; $firstTeamIndex++)
    {
        for ($secondTeamIndex = $firstTeamIndex + 1; $secondTeamIndex -lt $teams.Count; $secondTeamIndex++)
        {
            #POSTSCRIPT-OOPS - nitpick mostly. Reading MOW's solution has enlightened me
            #that, instead of creating objects and bolting properties onto them one at a time
            #as I do in the function referenced below, I could achieve the same result with
            #some well-constructed
            #    Select-Object @{Name=""; Expression={"stuff goes here"} }
            #statements.
            Create-GameObject -game "$($teams[$firstTeamIndex]) vs. $($teams[$secondTeamIndex])"
        }
    }
}


function Solve-Problem7
{
    $unsortedGames = Generate-RoundRobinGames


    Write-Host ($unsortedGames | sort RandomOrder | % { $_.Game } | out-string)
}


Solve-Problem7

2008 Winter Scripting Game Events: Index

Categories: PowerShell
Technorati:
Wednesday, April 23, 2008 3:46:13 AM UTC  #     |  Comments [0]  |  Trackback
Wednesday, April 23, 2008 3:45:50 AM UTC #

If you aren't already in the know, these ten problems are from the 2008 Winter Scripting games.

For each problem, I'm going to quickly sum up the interesting (interesting TO ME) bits of each problem, then I'm going to post the full source.

Event 6: Prime Time

Event: calculate primes up to 200. Solution: boring brute-force method.

Source

#PROBLEM #6


#As simple as possible, calculation of primes. This
#routine is inefficient.
function Is-Prime ($n)
{
    if ($n -le 1)
    {
        return $FALSE
    }
   
    $largestPossibleCoefficient = [int]([Math]::Floor($n/2))
    for ($i = 2; $i -le $largestPossibleCoefficient; $i++)
    {
        if ( ($n/$i) -eq ([Math]::Floor($n/$i)) )
        {
            return $FALSE
        }
    }
   
    return $TRUE;
}


function Solve-Problem6
{
    $range = 2..200
    foreach ($candidatePrime in $range)
    {
        if (Is-Prime $candidatePrime)
        {
            Write-Host $candidatePrime
        }
    }
}


Solve-Problem6

2008 Winter Scripting Game Events: Index

Categories: PowerShell
Technorati:
Wednesday, April 23, 2008 3:45:50 AM UTC  #     |  Comments [0]  |  Trackback