Why do I need to have my functions written first in my PowerShell script?

Viewed 47398

I have a script that I am utilizing functions to wrap parts of the code that allow me to move through the sections at a specified point. What I have found is that I have to have the functions listed first in the script for it to run correctly.

Non-working example

$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

# Steps.ps1 
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

Error

This give me the following error:

The term 'Step1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

 At C:\Tools\Scripts\functiontest.ps1:7 char:12
  +     1{Step1 <<<< }
  + CategoryInfo          : ObjectNotFound: (Step1:String) [], CommandNotFoundException
  + FullyQualifiedErrorId : CommandNotFoundException*

Working example

If I change the order around it works fine:

# Steps.ps1 
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

#steps
$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

Why?

I am guessing that it is because PS is not loading the functions.

Why is this and is there a better way to lay out this code structure?

7 Answers

A solution from Microsoft blog, Enclose the main code in a block and call in the end,

$MainFunction={
   $stepChoice = read-host 'Where would you like to start.'
   switch($stepChoice)
   {
       1{Step1}
       2{Step2}
       3{Step3}
   }
}
# Steps.ps1 
function Step1 { 
  'Step 1' 
   Step2 
} 
function Step2 { 
  'Step 2' 
   Step3 
} 
function Step3 { 
  'Step 3' 
  'Done!' 
}
#This line executes the program
& $MainFunction

Sorry, I had to comment. CMD\Batch does allow you to declare the functions below the main method just like C#.

@ECHO OFF
::Main
CALL :Function1
CALL :Function2
EXIT /b

::Functions
:Function1
    (ECHO Hello) & (ECHO World) 
EXIT /b

:Function2
    (ECHO Foo) & (ECHO Bar) 
EXIT /b 

With the addition of Powershell classes, you can now use methods in the place of functions.

Rough example, used static methods to make it easier to understand - more info: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.2#static-attribute

$stepChoice = read-host 'Where would you like to start.'

switch ($stepChoice)
{
    1
    {
        [Step1]::Step()
    }
    2
    {
        [Step2]::Step()
    }
    3
    {
        [Step3]::Step()
    }

}

# Steps.ps1
class Step1
{
    static [void]Step()
    {
        Write-Host 'Step 1'
        [Step2]::Step()
    }
}


class Step2
{
    static [void]Step()
    {
        Write-Host 'Step 2'
        [Step3]::Step()
    }
}


class Step3
{
    static [void]Step()
    {
        Write-Host 'Step 3'
        Write-Host 'Done!'
    }
}
Related