¿Por qué necesito que mis funciones se escriban primero en mi script de PowerShell?


Tengo un script que estoy utilizando funciones para envolver partes del código que me permiten moverme a través de las secciones en un punto específico. Lo que he encontrado es que tengo que tener las funciones listadas primero en el script para que se ejecute correctamente.

Ejemplo no funcional

$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

Esto me da el siguiente error:

El término 'Step1' no se reconoce como el nombre de un cmdlet, función, archivo de script o programa operable. Compruebe el ortografía del nombre, o si se incluyó una ruta, verifique que la ruta es correcta e inténtelo de nuevo.

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

Ejemplo de trabajo

Si cambio el orden, funciona bien:

# 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}

}

¿Por qué?

Supongo que es porque PS no está cargando las funciones.

¿Por qué es esto y hay una mejor manera de establecer esta estructura de código?

Author: StackzOfZtuff, 2010-10-12

4 answers

Reordena tu script

PowerShell es un script, no un lenguaje cumplido. Por lo tanto, pasa a través del script línea por línea, de arriba a abajo (después de tokenizar el script) y evalúa cada comando a lo largo del camino. Si aún no ha llegado a la definición de una función y ya está intentando invocar esa función, PowerShell generará un error.

Por lo tanto, en este caso debe mover las definiciones de función antes de la instrucción switch - como descubrir.

Declaraciones forward

Incluso algunos lenguajes compilados se comportan de esta manera, sobre todo C/C++, y requieren declaraciones forward para solucionar este problema.

Otros lenguajes compilados como C# hacen varias pasadas sobre el código durante la compilación, por lo que no se requieren declaraciones forward.

 24
Author: Keith Hill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-15 18:07:24

Recuerde que, en general, lo que funciona en un script debe funcionar en la línea de comandos.

Esto no era cierto en CMD. GOTO y FOR %I IN (...) DO %%I son dos ejemplos.

En PowerShell, puedo ejecutar comandos en la línea de comandos hasta obtener el resultado que desee, luego pegar el historial en un script y luego editar los bits extraños.

Además, puedo tomar un script que no funciona correctamente, pegarlo en un shell interactivo y estudiar el estado resultante.

En el línea de comandos interactiva, no hay manera de que pueda escribir esto:

F
function F { "Hello, World!" }

Sin embargo, al leer un script, primero quiero leer el código de nivel superior y luego ver más detalles mientras me desplazo hacia abajo. Un enfoque es:

function Main 
{
    F
}

function F
{
    "Hello, World!"
}

Main
 46
Author: Jay Bazuzi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-23 11:11:48

También puede obtener sus definiciones de función de un archivo separado:

Steps-Lib. ps1

# Since this is just function definitions it is safe to source
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

Steps. ps1

# This sources the Steps-Lib.ps1 so that the functions are available
. "./Steps-Lib.ps1"

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

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}
}
 7
Author: Simon Hartcher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-02 03:57:28

Además de lo que Keith dijo sobre el orden del intérprete, también es parte del diseño de Powershell. Su realmente pretende comportarse como una interfaz para Objetos CLR e incluso sus propios cmdlets. Por lo tanto, en el "scripting" de Powershell se está menos construyendo esta lista masivamente compleja de acciones a realizar y más armando una colección de otras piezas más pequeñas de lógica y definiendo cómo interactuar con ellas.

Sin entrar en una discusión cuasi religiosa de Powershell y OOP, la más fácil la manera de lograr lo que quieres es enterrar todas tus funciones en un archivo separado (llámalo functions.ps1) y luego incluirlo al principio.

Así que suponiendo que todo estaba en functions1. ps1

Hacer un

$functions = "$($MyInvocation.MyCommand.path | split-path)\functions.ps1"
. $functions

Entonces

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

}

Funcionaría bien

 2
Author: Taylor Bird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-10 10:47:31