How do I define functions within a CmdletBinding() script?

3.3k views Asked by At

I'm writing a script that I'd like to use PowerShell's CmdletBinding() with. Is there a way to define functions in the script? When I try, PowerShell complains about "Unexpected toke 'function' in expression or statement"

Here's a simplified example of what I'm trying to do.

[CmdletBinding()]
param(
    [String]
    $Value
)

BEGIN {
    f("Begin")
}

PROCESS {
    f("Process:" + $Value)
}

END {
    f("End")
}

Function f() {
    param([String]$m)
    Write-Host $m
}

In my case, writing a module is wasted overhead. The functions only need to be available to this one script. I don't want to have to mess with the module path or the script location. I just want to run a script with functions defined in it.

1

There are 1 answers

2
Ansgar Wiechers On BEST ANSWER

You use begin, process, and end blocks when your code is supposed to process pipeline input. The begin block is for pre-processing and runs a single time before processing of the input starts. The end block is for post-processing and runs a single time after processing of the input is completed. If you want to call a function anywhere other than the end block you define it in the begin block (re-defining it over and over again in the process block would be a waste of resources, even if you didn't use it in the begin block).

[CmdletBinding()]
param(
    [String]$Value
)

BEGIN {
    Function f() {
        param([String]$m)
        Write-Host $m
    }

    f("Begin")
}

PROCESS {
    f("Process:" + $Value)
}

END {
    f("End")
}

Quoting from about_Functions:

Piping Objects to Functions

Any function can take input from the pipeline. You can control how a function processes input from the pipeline using Begin, Process, and End keywords. The following sample syntax shows the three keywords:

function <name> { 
    begin {<statement list>}
    process {<statement list>}
    end {<statement list>}
}

The Begin statement list runs one time only, at the beginning of the function.

The Process statement list runs one time for each object in the pipeline. While the Process block is running, each pipeline object is assigned to the $_ automatic variable, one pipeline object at a time.

After the function receives all the objects in the pipeline, the End statement list runs one time. If no Begin, Process, or End keywords are used, all the statements are treated like an End statement list.


If your code doesn't process pipeline input you can drop begin, process, and end blocks entirely and put everything in the script body:

[CmdletBinding()]
param(
    [String]$Value
)

Function f() {
    param([String]$m)
    Write-Host $m
}

f("Begin")
f("Process:" + $Value)
f("End")

Edit: If you want to put the definition of f at the end of your script you need to define the rest of your code as a worker/main/whatever function and call that function at the end of your script, e.g.:

[CmdletBinding()]
param(
    [String]$Value
)

function Main {
    [CmdletBinding()]
    param(
        [String]$Param
    )

    BEGIN   { f("Begin") }
    PROCESS { f("Process:" + $Param) }
    END     { f("End") }
}

Function f() {
    param([String]$m)
    Write-Host $m
}

Main $Value