Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Find more tips in the Windows PowerShell Tip of the Week archive.

Three Things You Might Not Know About Windows PowerShell Functions

What’s that? You say you know everything there is to know about using functions in Windows PowerShell? Well, good for you. But, just in case, you might check out the three things that the Scripting Guys know about using functions in Windows PowerShell.

You can set default values for function parameters

As a general rule, any time you call a function you pass it the appropriate number of parameters. For example, suppose we have the following function, a function that multiplies two numbers:

function test ($x, $y)
    {
        $x * $y
    }

To use this function, you typically provide values for the variables $x and $y at the time you call the function:

test 33 22

That’s fine; that’s exactly what you should do when you call a function. However, consider the following function, one that reports back the day of the year for a given date:

function test ($x)
    {
        (Get-Date $x).DayOfYear
    }

To call this function, you use a command similar to this:

test "6/27/2008"

So far so good. On the other hand, suppose that, more often than not, any time you want to get the day of the year it’s for the current date. Ideally, then, you could simply call the function without any parameters and, by default, the function would return the day of the year for the current date:

test

So how can you set a default value in a function? Why, like this, of course:

function test ($x = (Get-Date).Date)
    {
        (Get-Date $x).DayOfYear
    }

As you can see, what we’ve done here is pre-assign a value to the function variable $x; in particular, we’ve assigned it the Date property of the current date (which we can retrieve using the Get-Date cmdlet). If we supply a parameter when we call this function then the value of that parameter will be assigned to $x. However, if we don’t supply a parameter when we call the function then $x will be assigned the default value: the current date. Give both of these commands a try and see what you get back:

test "6/27/2008"
test

Incidentally, you can also assign a specific data type to a function variable. For example, in this function we’ve assigned the integer data type ([int]) to the function variables $x and $y:

function test ([int] $x = 2, [int] $y = 8)
    {
         $x * $y
    }

Now, what do you suppose we’ll get back if we call the function using this command:

test 4.332 3.7181

That’s right: we’ll get back the value 12. Why? Because PowerShell will convert each of the parameter values (4.332 and 3.7181) to integers (4 and 3, respectively) before multiplying them.

You might also have noticed that we assigned $x a default value of 2 and $y a default value of 8. Suppose we call this function without supplying any parameters. In that case we – that’s right, we get back 16, because the function will multiply the default values 2 and 8. (It always takes us by surprise when we discover that someone’s been paying attention to us!) Now, what will we get back if we call the function using this command:

test 11

If you said 88, well, give yourself a star. (No, not a gold star, just a star. It wasn’t that hard of a question.) Why 88? Well, for one thing, the parameter value 11 will be assigned to $x; the first parameter supplied to the function will always be assigned to $x. And because we didn’t bother with a second parameter, $y takes on the default value of 8. And 11 * 8 is … that’s right, 88.

What the heck: go ahead and give yourself a gold star. You earned it.

Note. What if we supplied three parameters (or more) to this function? Well, nothing bad will happen: the function will ignore the extra parameters and multiply parameter 1 by parameter 2. However, those parameter values are neither gone nor forgotten: all “extra” parameter values are stored in the variable $args. That means these values are available, if you have some use for them.

By the way, remember when we said that the “…first parameter supplied to the function will always be assigned to $x”? Well, that’s only partly true. As it turns out, if you call your function using named parameters then you can specify which value is assigned to which function variable, regardless of the order in which those values are supplied.

Good question: what the heck does that mean? Well, let’s take a look at yet another function:

function test
    {
        param ($x = 5, $y = 14)
         $x * $y
    }

Here we’ve used the param statement to declare our two function variables and their default values; notice that the param statement is included within the function’s scriptblock. Suppose we call the function using this command:

test 4

In this case, the value 4 will be assigned to $x and $y will take on the default value of 14. As a result, the function returns the value 56.

But what if we wanted the value 4 to be assigned to $y? That’s fine; we just have to indicate that when we call the function:

test -y 4

See? In this case we explicitly assigned the value 4 to $y. This time around $y will be equal to 4, $x will be assigned the default value 5, and the function will return 20. Cool, huh? And if you want to assign values to both variables, well, then assign values to both variables, without worrying about the order of assignment:

test -y 4 -x 22

You can pipe data to a function

When you first start out with Windows PowerShell someone is likely to tell you that “Only a cmdlet can be on the receiving end of a pipeline.” What does that mean? Well, newcomers to PowerShell often write commands like this one, thinking that somehow this command will assign the current date and time to the variable $x:

Get-Date | $x

However, this is not going to assign the current date and time to $x; instead, it’s going to result in the dreaded error message:

Expressions are only permitted as the first element of a pipeline.
At line:1 char:14
+ get-date | $x <<<<

You get this error message because you have a variable ($x) on the receiving end of the pipeline, and that’s a no-no. You must have a cmdlet on the receiving end of the pipeline.

Or at least that’s what they tell you when you first start out. Now that you’re a little older and a little more experienced, it’s time to learn the facts of life: as long as you know a little trick then you can place a function on the receiving end of a pipeline.

So what is this trick we’re talking about? It has to do with the $input variable, an automatic variable that holds all the data passed through the pipeline. Let’s take a look at a function that can be used on the receiving end of a pipeline:

function test
    {
        foreach ($i in $input)
            {$i.ToUpper()}
    }

As you can see, our first command is a standard foreach loop. But take a closer look at what we’re looping through; we’re looping through the values in $input. And then, for each of those values, we simply use the ToUpper method to convert the values to their uppercase equivalent.

In other words, suppose we run the following command:

"a", "b", "c"  | test

Here’s what we’ll get back:

A
B
C

You can make a function available in other scopes

OK, so you already knew that; after all, you can make a function global (that is, available in other scopes) simply by placing that function in your PowerShell profile or by “dot sourcing” the script that contains that function. But here’s another way to make a function global; just preface the function name with the keyword global:

function global:test ($x, $y)
    {
         $x * $y
    }

That’s all there is to it.

Bonus tip: View the code for your global functions

Got a function named test, but you can’t remember what test actually does? In that case, use the Get-Content cmdlet to retrieve the function code:

Get-Content function:\test