Write Functions, Not Scripts – Part III

First off, I have to say, the first two posts in this series have been very successful. Nearly 200 visitors on a Saturday and then a Sunday, too — that’s unheard of during the weekend. And to think, these two posts: Part I and Part II, have felt like some of the most distracted and unpredictable writing of mine to date, and I’ve been at this for almost four years straight, without a missed month.

Okay, to continue with a distraction, in Write Functions, Not Scripts — Part II, I showed a picture of the fog on my cul-de-sac in Tucson, AZ (the desert) last Friday. Here’s that picture now. That fog, is not typical for us. Maybe it happens once a year.

I mentioned that I’d include the typical view, and so here’s a picture from Saturday — just one day later. And that’s, what it normally looks like around here.

Moving along, as if there’s a PowerShell related topic at hand. So, here’s one reason for functions, over scripts: it’s variables. Let’s move out the fog, and explain.

When we’re developing scripts, we often create variables and assign them values at the top of our code. Normal stuff. The problem is that when we go to execute the script a second (or third, or fourth) time, the variables in our script have already been assigned a value, and that may make our results incorrect. This can lead to confusion and wasted time, incorrect results, and extra lines of unnecessary commands to initialize variables. Stop initializing, and then re-initializing, variables.

Let’s take a look at an example.

In each pass though the below ForEach-Object cmdlet, we set the $User variable to my name with the number of the current pass at the end. Then, we increase the $Count variable by 1. Take a look at the below results. Everything looks perfect. My name includes the current iteration through the loop, and the count matches, as it should.

1..5 | ForEach-Object {
    $User = "tommymaynard$_"
    $Count++

    "Count: $($Count)"
    "User: $User"
}

Count: 1
User: tommymaynard1
Count: 2
User: tommymaynard2
Count: 3
User: tommymaynard3
Count: 4
User: tommymaynard4
Count: 5
User: tommymaynard5

Now, let’s run it again.

1..5 | ForEach-Object {
    $User = "tommymaynard$_"
    $Count++

    "Count: $($Count)"
    "User: $User"
}

Count: 6
User: tommymaynard1
Count: 7
User: tommymaynard2
Count: 8
User: tommymaynard3
Count: 9
User: tommymaynard4
Count: 10
User: tommymaynard5

We didn’t want to continue to increase the $Count variable past five, but because we’re scripting, we did it anyway. Writing something like this would required a Remove-Variable, or Clear-Variable command, when the final loop was done. Annoying, and without it, when you’re scripting, you’re going to have inaccuracies.

With functions, we don’t ever need to reinitialize variables to their default values. And of course, we don’t need to remove them, or clear them, before our code is run a second, or third time. This isn’t to say you’ll never change the value of a variable inside a function, it’s to say that variables in a function don’t typically exist before, or after, a function is executed.

Function Show-CountAndUser {
    1..5 | ForEach-Object {
        $User = "tommymaynard$_"
        $Count++

        "Count: $($Count)"
        "User: $User"
    }
}

PS > Show-CountAndUser
Count: 1
User: tommymaynard1
Count: 2
User: tommymaynard2
Count: 3
User: tommymaynard3
Count: 4
User: tommymaynard4
Count: 5
User: tommymaynard5

Okay, that was the first execution. One through five: it looks good. And here’s, the second execution.

PS > Show-CountAndUser
Count: 1
User: tommymaynard1
Count: 2
User: tommymaynard2
Count: 3
User: tommymaynard3
Count: 4
User: tommymaynard4
Count: 5
User: tommymaynard5

Ah, perfect! Write functions, and stop worrying about having to set, and reset, variables. Use the variable scope provided by functions — it wants to help you. It’s one reason for functions over scripts. Back again later.

Part IV is now available.

Leave a Reply

Your email address will not be published. Required fields are marked *