Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to make certain functions "private" in a PowerShell script?

When my shell starts, I load an external script that has a few functions I use to test things. Something like:

# Include Service Test Tools
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
. $scriptPath\SvcTest.ps1

In SvcTest.ps1, I have two functions:

function isURI ([string] $address)
{
   ($address -as [System.URI]).AbsoluteURI -ne $null
}

As well as:

function Test-Service ([string] $url)
{
   if (-Not (isURI($url)))
   {
      Write-Host "Invalid URL: $url"
      return
   }

   # Blah blah blah, implementation not important
}

The isURI function is basically just a utility function that allows Test-Service and perhaps other functions validate URIs. However, when I start my shell, I see that isURI is a function loaded globally. I can even type isURI http://www.google.com from the command line and get back True.

My Question: Is there a way to make isURI private, so that only functions within SvcTest.ps1 can use it, while still allowing Test-Service to be global? Basically, I'm looking for a way to use property encapsulation within PowerShell scripts.

like image 949
Mike Christensen Avatar asked Aug 04 '14 17:08

Mike Christensen


People also ask

What is Dot sourcing in PowerShell?

The dot sourcing feature lets you run a script in the current scope instead of in the script scope. When you run a script that is dot sourced, the commands in the script run as though you had typed them at the command prompt.

What is a PowerShell module manifest?

A module manifest is a PowerShell data file ( . psd1 ) that describes the contents of a module and determines how a module is processed. The manifest file is a text file that contains a hash table of keys and values.

How do I call a PowerShell function from another PowerShell script?

If you want to execute a function from another PowerShell script file, you can “dot-source” the file. The “.” is a special operator in PowerShell that can load another PowerShell script file en import all code in it.


3 Answers

In fact, if you call a .ps1 file, by default any functions and variables declared within it are scoped privately within the script (this is referred to as "script scope"). Since you're seeing both functions defined globally, I infer that you're dot-sourcing SvcTest.ps1, i.e. invoking it like this

PS> . <path>\SvcTest.ps1

rather than calling it like this

PS> <path>\SvcTest.ps1


You have two options.

1. If your private function is only used by one other function in the script, you can declare the private function within the body of the function that uses it, and invoke the script by dot-sourcing it:

function Test-Service ([string] $url)
{
    function isURI ([string] $address)
    {
        ($address -as [System.URI]).AbsoluteURI -ne $null
    }

    if (-Not (isURI($url)))
    {
        Write-Host "Invalid URL: $url"
        return
    }

    # Blah blah blah, implementation not important
}

2. If the private function is needed by more than one other function within the script (or even if not, this is an alternative to the above), explicitly declare global scope for any functions that you want defined globally, and then call the script rather than dot-sourcing it:

function isURI ([string] $address)
{
   ($address -as [System.URI]).AbsoluteURI -ne $null
}


function global:Test-Service ([string] $url)
{
   if (-Not (isURI($url)))
   {
      Write-Host "Invalid URL: $url"
      return
   }

   # Blah blah blah, implementation not important
}

In either case, Test-Service will be defined in the global scope, and isURI will be restricted to the script scope.


* One thing that might confuse the issue here is that PowerShell only looks for executables in the path, not the current working directory, unless . has been added to the path (which is not the case by default). So, it's typical in PowerShell when invoking scripts in the working directory to precede the script name with .\. Don't confuse the . representing the working directory with the dot-sourcing operator. This calls a script:
PS> .\SvcTest.ps1

This dot-sources it:

PS> . .\SvcTest.ps1

like image 180
Adi Inbar Avatar answered Oct 19 '22 06:10

Adi Inbar


It sounds to me like you're asking for functionality that's available by creating a module.

Modules let you encapsulate code and export only desired aliases and/or functions. A module manifest is not strictly required; if you don't use a manifest, you can use Export-ModuleMember to specify what members you want exported from the module.

See the help about_Modules about topic for more information.

like image 21
Bill_Stewart Avatar answered Oct 19 '22 05:10

Bill_Stewart


If you want to use a private scope for your function, it is done like this in Powershell.

function Private:isURI ([string] $address)
{
   ($address -as [System.URI]).AbsoluteURI -ne $null
}
like image 5
Knuckle-Dragger Avatar answered Oct 19 '22 06:10

Knuckle-Dragger