Controlling PowerShell Function (Re)Definition

A question came up in the PowerShell news group recently where someone was asking how they could prevent functions from being redefined.

 

There are a couple of approaches you can use. First you can make functions read-only or constant. This is pretty straight-forward but does have some downsides. The other approach is to check to see if the function exists and only create it if it isn’t already defined.

 

First we’ll look at defining read-only or constant functions. There isn’t a way of doing this with the function keyword in V1.0 of PowerShell. Instead, you have to do this through the function namespace provider using Set-Item (exactly the same way you would make a file read-only by the way):

 

PS (7) > set-item function:bar {“Bar”} -option readonly

PS (8) > function bar {“Dude!”}

Cannot write to function bar because it is read-only or constant.

At line:1 char:13

+ function bar <<<< {“Dude!”}

 

As we can see, trying to define a read-only function will fail. You can, however, use the -force option to force a redefinition:

 

PS (9) > set-item function:bar {“Dude”} -force

PS (10) > bar

Dude

 

The other option is to make a function constant when it is defined:

 

PS (11) > set-item function:bar {“Constant”} -option constant

Set-Item : Existing function bar cannot be made constant. Functions can be made constant only at creation time.

At line:1 char:9

+ set-item <<<< function:bar {“Constant”} -option constant

 

This fails initially because we’ve already defined “bar” . Use Remove-Item to remove the function, and then it works:

 

PS (12) > Remove-Item function:bar -force

PS (13) > set-item function:/bar {“Constant”} -option constant

PS (14) > set-item function:/bar {“Dude”} -force

Set-Item : Cannot write to function bar because it is read-only or constant.

At line:1 char:9

+ set-item <<<< function:/bar {“Dude”} -force

 

Note that you can’t redefine a constant function, even with –force. A constant function will persist until the process terminates.

 

So that’s how you define a constant function.

 

Attempting to redefine a costant or read-only function will result in errors. This may be fine, but there are times such as when you are processing the profile files or “dotting” a library script that you don’t want to see errors. In this situation, a better way of handling this is to only redefine a function if it doesn’t already exist.

 

In PowerShell, functions are bound at runtime. In other words, while the syntax of the function is checked when the file is loaded (complied), the function isn’t associated with the name until the script is executed. This makes it pretty easy to control when functions are bound. You can use the Test-Path cmdlet to check to see if the function exists and only execute the function statement if it doesn’t already exist as shown:

 

PS (17) > if (! (test-path function:buz)) { function buz { “First” } }

PS (18) > buz

First

PS (19) > if (! (test-path function:buz)) { function buz { “Second” } }

PS (20) > buz

First

 

The first statement above establishes the initial binding for the buz. The second statement checks the definition and doesn’t override it since it already exists.

 

Another way to do this is with the New-Item cmdlet. This cmdlet will only create a new item in a namespace if it doesn’t already exist:

 

PS (45) > new-item -path function: -name buz -value {“Hi”}

New-Item : The item at path ‘buz’ already exists.

At line:1 char:9

+ new-item <<<< -path function: -name buz -value {“Hi”}

 

We’re still seeing an error which we don’t want so we can use the –ErrorAction parameter to suppress this:

 

PS (46) > new-item -path function: -name buz -value {“Hi”} -ea silentlycontinue

PS (47) > buz

First

 

And we can see that the definition hasn’t changed.

 

So now we know how to control when functions are defined and redefined. The cool thing about this is that we also now know how to do the same thing with variables. In fact we can do the same thing with any provider the supports the read-only and constant options. And even if the provider doesn’t support these features, you can still use Test-Path to prevent over-writing existing items.

 

-bruce

 

Bruce Payette [MSFT]

Windows PowerShell Tech Lead

 

Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell

Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Windows PowerShell in Action (book): http://manning.com/powershell