net management framework powershell powershell-v5.0

management - Cómo exportar una clase en el módulo PowerShell v5



net framework 5 (10)

Tengo una configuración de módulo para ser como una biblioteca para algunos otros scripts. No puedo averiguar cómo obtener una declaración de clase en el ámbito del script que llama a Import-Module . Intenté organizar Export-Module con un argumento de -class , como la -function , pero no hay una -class disponible. ¿Tengo que declarar la clase en cada script?

La puesta en marcha:

  • holidays.psm1 en ~ / documents / windows / powershell / modules / holidays /
  • script activo llama import-module holidays
  • hay otra función en holidays.psm1 que devuelve un objeto de clase correctamente, pero no sé cómo crear nuevos miembros de la clase desde el script activo después de importar

Aquí es cómo se ve la clase:

Class data_block { $array $rows $cols data_block($a,$r,$c) { $this.array = $a $this.rows = $r $this.cols = $c } }


Encontré una forma de cargar las clases sin la necesidad de "usar el módulo". En su archivo MyModule.psd1 use la línea:

ScriptsToProcess = @(''Class.ps1'')

Y luego ponga sus clases en el archivo Class.ps1:

class MyClass {}

Actualización: Aunque no tiene que usar "utilizando el módulo MyModule" con este método, todavía tiene que:

  • Ejecutar "usando el módulo MyModule"
  • O ejecute "Import-Module MyModule"
  • O llame a cualquier función en su módulo (para que importe automáticamente su módulo en el camino)

Esto ciertamente no funciona como se esperaba.
La idea en PS 5 es que puedes definir tu clase en un archivo separado con una extensión .psm1.
Luego puede cargar la definición con el comando (por ejemplo):

using module C:/classes/whatever/path/to/file.psm1

Esta debe ser la primera línea en su script (después de los comentarios).


Lo que causa tanto dolor es que incluso si las definiciones de clase son llamadas desde un script, los módulos se cargan para toda la sesión. Puedes ver esto ejecutando:

get-module

Verás el nombre del archivo que cargaste. No importa si vuelve a ejecutar el script, ¡ NO volverá a cargar las definiciones de clase! (Ni siquiera leerá el archivo psm1). Esto causa mucho crujir de dientes.


A veces , a veces , puede ejecutar este comando antes de ejecutar el script, que volverá a cargar el módulo con las definiciones de clase actualizadas:

remove-module file

donde archivo es el nombre sin ruta o extensión. Sin embargo, para guardar la cordura, recomiendo reiniciar la sesión de PS. Esto es obviamente engorroso; Microsoft necesita limpiar esto de alguna manera.


He intentado todos los enfoques en esta página y AÚN tengo que encontrar uno que funcione repetidamente. Creo que el almacenamiento en caché de módulos es el aspecto más frustrante de esto ... Puedo importar clases como módulos e incluso hacer que el código funcione, pero powershell solo reconoce la nueva clase de forma intermitente ... es casi como si Powershell olvidara que alguna vez agregaste una. .


La declaración de using es el camino a seguir si funciona para usted. De lo contrario, esto parece funcionar también.

testclass.psm1

Usa una función para entregar la clase.

class abc{ $testprop = ''It Worked!'' [int]testMethod($num){return $num * 5} } function abc(){ return [abc]::new() } Export-ModuleMember -Function abc

someScript.ps1

Import-Module path/to/testclass.psm1 $testclass = abc $testclass.testProp # returns ''It Worked!'' $testclass.testMethod(500) # returns 2500 $testclass | gm Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() testMethod Method int testMethod(System.Object num) ToString Method string ToString() testprop Property System.Object testprop {get;set;}


La forma en que he resuelto este problema es mover su definición de clase personalizada a un archivo .ps1 vacío con el mismo nombre (como lo haría en Java / C #), y luego cargarlo en la definición del módulo y su código dependiente. fuente de puntos. Sé que esto no es bueno, pero para mí es mejor que tener que mantener varias definiciones de la misma clase en varios archivos ...


Para actualizar las definiciones de clase mientras se desarrolla, seleccione el código de la clase y presione F8 para ejecutar el código seleccionado. No tan limpio como la opción -Force en el comando Import-Module . Al ver que el Module Uso no tiene esa opción y Remove-Module es, en el mejor de los casos, esporádico, esta es la mejor manera que he encontrado para desarrollar una clase y ver los resultados sin tener que cerrar el ISE y volver a iniciarlo.


Según here y here , puede usar las clases definidas en su módulo haciendo lo siguiente en PowerShell 5:

using module holidays


También he encontrado varios problemas relacionados con las clases de PowerShell en v5.

He decidido usar la siguiente solución por ahora, ya que es perfectamente compatible con .net y PowerShell:

Add-Type -Language CSharp -TypeDefinition @" namespace My.Custom.Namespace { public class Example { public string Name { get; set; } public System.Management.Automation.PSCredential Credential { get; set; } // ... } } "@

La ventaja es que no necesita un ensamblaje personalizado para agregar una definición de tipo, puede agregar la definición de clase en línea en sus scripts o módulos de PowerShell.

El único inconveniente es que deberá crear un nuevo tiempo de ejecución para volver a cargar la definición de la clase después de que se haya cargado por primera vez (al igual que cargar los ensamblajes en el dominio ac # / .net).


Usted casi no puede. De acuerdo con la ayuda de about_Classes :

Palabra clave de clase

Define una nueva clase. Este es un verdadero tipo de .NET Framework. Los miembros de la clase son públicos, pero solo públicos dentro del alcance del módulo. No puede referirse al nombre del tipo como una cadena (por ejemplo, New-Object no funciona), y en esta versión, no puede usar un literal de tipo (por ejemplo, [MyClass]) fuera del script / Archivo de módulo en el que se define la clase.

Esto significa que, si desea obtener una instancia de data_block o usar funciones que operen esas clases, New-DataBlock una función, por ejemplo, New-DataBlock y haga que devuelva una nueva instancia de data_block , que luego puede usar para obtener métodos y propiedades de clase ( probablemente incluyendo los estáticos).


PSA: hay un problema conocido que mantiene copias antiguas de las clases en la memoria. Hace que el trabajar con clases sea realmente confuso si no lo sabes. Puedes leer sobre esto here .

using es propenso a errores

La palabra clave de using es propensa a varias trampas de la siguiente manera:

  • La declaración de using no funciona para los módulos que no están en PSModulePath menos que especifique la ruta completa del módulo en la declaración de using . Esto es bastante sorprendente porque aunque un módulo está disponible a través de Get-Module la instrucción de using puede no funcionar dependiendo de cómo se cargó el módulo.
  • La declaración de using solo se puede utilizar al principio de un "script". Ninguna combinación de [scriptblock]::Create() o New-Module parece superar esto. Una cadena pasada a Invoke-Expression parece actuar como una especie de script independiente; una declaración de using al principio de este tipo de cadena de trabajos. Es decir, Invoke-Expression "using module $path" puede tener éxito, pero el alcance en el que los contenidos del módulo están disponibles parece bastante inescrutable. Por ejemplo, si se Invoke-Expression "using module $path" se usa dentro de un bloque de script Pester, las clases dentro del módulo no están disponibles en el mismo bloque de script Pester.

Las declaraciones anteriores se basan en este conjunto de pruebas .

ScriptsToProcess impide el acceso a las funciones de los módulos privados

A primera vista, la definición de una clase en un script al que hace referencia el ScriptsToProcess del manifiesto del ScriptsToProcess parece exportar la clase desde el módulo. Sin embargo, en lugar de exportar la clase, "crea la clase en el SessionState global en lugar del módulo, por lo que ... no puede acceder a las funciones privadas" . Por lo que puedo decir, usar ScriptsToProcess es como definir la clase fuera del módulo de la siguiente manera:

# this is like defining c in class.ps1 and referring to it in ScriptsToProcess class c { [string] priv () { return priv } [string] pub () { return pub } } # this is like defining priv and pub in module.psm1 and referring to it in RootModule New-Module { function priv { ''private function'' } function pub { ''public function'' } Export-ModuleMember ''pub'' } | Import-Module [c]::new().pub() # succeeds [c]::new().priv() # fails

Invocando esto resulta en

public function priv : The term ''priv'' is not recognized ... + [string] priv () { return priv } ...

La función de módulo priv es inaccesible desde la clase aunque se llama priv desde una clase que se definió cuando se importó ese módulo. Esto podría ser lo que desea, pero no he encontrado un uso para él porque he encontrado que los métodos de clase generalmente necesitan acceso a alguna función en el módulo que deseo mantener en privado.

.NewBoundScriptBlock() parece funcionar de manera confiable

La invocación de un bloque de script vinculado al módulo que contiene la clase parece funcionar de manera confiable para exportar instancias de una clase y no sufre los inconvenientes que el using de la clase. Considere este módulo que contiene una clase y se ha importado:

New-Module ''ModuleName'' { class c {$p = ''some value''} } | Import-Module

Al invocar [c]::new() dentro de un bloque de script enlazado al módulo se produce un objeto de tipo [c] :

PS C:/> $c = & (Get-Module ''ModuleName'').NewBoundScriptBlock({[c]::new()}) PS C:/> $c.p some value

Alternativa idiomática para .NewBoundScriptBlock()

Parece que hay una alternativa idiomática más corta a .NewBoundScriptBlock() . Las dos líneas siguientes invocan el bloque de secuencias de comandos en el estado de sesión de la salida del módulo mediante Get-Module :

& (Get-Module ''ModuleName'').NewBoundScriptBlock({[c]::new()}) & (Get-Module ''ModuleName'') {[c]::new()}}

El último tiene la ventaja de que dará un flujo de control al bloque de secuencia de comandos central de la canalización cuando se escriba un objeto en la canalización. .NewBoundScriptBlock() otra parte, .NewBoundScriptBlock() recopila todos los objetos escritos en la canalización y solo produce una vez que se ha completado la ejecución de todo el bloque de scripts.