2
votes

Fonctions PowerShell chargées à partir de la fonction

J'ai un module avec plusieurs fichiers avec des fonctions et un chargeur de module.

L'exemple de fonction:

Function1.ps1

Get-ChildItem $PSScriptRoot\*.ps1 | %{
    . $import.FullName
}
Init # In this case - all works fine


5 commentaires

La fonction dot-sources les fichiers dans une portée différente. Pourquoi avez-vous besoin de cette fonction wrapper en premier lieu? Faites simplement ce que vous faites dans votre dernier extrait de code.


En fait, le module charge les fichiers de fonction privés / publics et partagés et j'ai déplacé le code vers une fonction séparée pour ne pas dupliquer le code car LoadModule contient une logique supplémentaire comme la gestion des erreurs et la journalisation


Si vous devez appeler Init une seule fois, vous pouvez le supprimer après l'avoir appelé.


@PetSerAl pourriez-vous expliquer plus en détail la suppression après l'appel? Ce serait peut-être la solution


Fonction Remove-Item: \ Init


3 Réponses :


2
votes

Remarque: L'édition 1, une clarification sur ce que fait réellement l'approvisionnement en points, est incluse à la fin.

Tout d'abord, vous mélangez la terminologie et l'utilisation des fonctions et des modules. Les modules, qui ont l'extension .psm1, doivent être importés dans le terminal à l'aide de la cmdlet Import-Module. Lors de la recherche de points, comme ce que vous faites ici, vous ne devriez cibler que les fichiers de script contenant des fonctions, qui sont des fichiers avec l'extension .ps1.

Moi aussi, je suis relativement nouveau dans PowerShell, et j'ai couru dans le même problème. Après avoir passé environ une heure à lire sur le problème, je n'ai pas pu trouver de solution, mais beaucoup d'informations que j'ai trouvées indiquent qu'il s'agit d'un problème de portée. J'ai donc créé un test, en utilisant trois fichiers.

foo.ps1

Write-Output "I am foo"
Write-Output "I am bar"

bar.psm1 code>

Write-Output "I am bar"

scoping.ps1

. ".\LoadModule.ps1"
LoadModule ".\"
foo
bar

Voyons ce que fait ce script.

Nous définissons d'abord une fonction factice loader . Ce n'est pas un chargeur pratique, mais il est suffisant pour tester les portées et la disponibilité des fonctions dans les fichiers chargés. Cette fonction dot source le fichier ps1 contenant la fonction foo, et utilise Import-Module pour le fichier contenant la barre de fonctions.

Ensuite, nous appelons les fonctions foo et bar , qui produiront des erreurs, afin d'établir que ni l'un ni l'autre ne sont dans la portée actuelle. Bien que cela ne soit pas strictement nécessaire, cela permet d'illustrer leur absence.

Ensuite, nous appelons la fonction loader . Après le sourcing de points foo.ps1 , nous voyons foo exécuté avec succès car foo est dans la portée actuelle du loader fonction. Après avoir utilisé Import-Module pour bar.psm1 , nous voyons bar également exécuté avec succès. Maintenant, nous quittons la portée de la fonction loader et retournons au script principal.

Maintenant, nous voyons que l'exécution de foo échoue avec une erreur. C'est parce que nous avons pointé foo.ps1 dans le cadre d'une fonction. Cependant, comme nous avons importé bar.psm1 , la barre s'exécute avec succès. Cela est dû au fait que les modules sont importés dans la portée globale par défaut.


Comment pouvons-nous utiliser cela pour améliorer votre fonction LoadModule ? L'essentiel pour cette fonctionnalité est que vous devez passer à l'utilisation de modules pour vos fonctions importées. Notez que, d'après mes tests, vous ne pouvez pas importer-module la fonction de chargeur; cela ne fonctionne que si vous dotez la source du chargeur.

LoadModule.ps1

function LoadModule($Path) {
    Get-ChildItem -Path "$Path" -Filter "*.psm1" -Recurse -File -Name| ForEach-Object {
        $File = "$Path$_"
        echo "Import-Module -Name $File"
        Import-Module -Name "$File" -Force
    }
}

Et maintenant dans un terminal: p>

function loader {
    echo "dot sourcing file"
    . ".\foo.ps1"
    foo
    echo "Importing module"
    Import-Module -Name ".\bar.psm1"
    bar
}

foo
bar

loader

foo
bar

pause

Edit 1: Une clarification supplémentaire sur le dot sourcing

Dot sourcing équivaut à copier-coller le contenu du fichier spécifié dans le fichier préformé la source de points. Le fichier effectuant l'opération "importe" le contenu de la cible verbatim, n'effectuant aucune action supplémentaire avant de procéder à l'exécution du code "importé". par exemple.

foo.ps1 Écriture-Sortie "Je suis foo"
. ". \ bar.ps1"

bar.ps1

function bar {
  Write-Output "bar"
}

est effectivement

XXX


4 commentaires

La syntaxe Import-Module ne me convient pas car le Module1.psm1 c'est en fait un module dont le but est de charger d'autres fonctions dans le module. Et si le module utilise d'autres fonctions d'importation avec Import-Module , le module ne pourra pas les exporter vers l'environnement.


Au fait, merci beaucoup pour l'explication détaillée de la "charge des modules" et de la "syntaxe des points"


Si vous l'utilisez comme je l'ai décrit, cela devrait fonctionner pour votre cas d'utilisation. Créez un fichier .ps1 contenant votre chargeur de module, puis appelez le chargeur de module pour importer vos fonctions restantes en tant que modules. L'importation d'un module qui importe ensuite plus de modules ne semble pas fonctionner. Trouver une fonction qui importe ensuite des modules fonctionne.


Je dois également noter que les fichiers .ps1 et .psm1 sont techniquement les mêmes. Il ne vous manque rien. La distinction est sémantique, ce qui encourage de meilleures pratiques de code. Votre code réutilisable doit se trouver dans des modules que vous importez et votre code spécifique au cas d'utilisation doit être dans des fichiers .ps1 que vous exécutez. Ces fichiers .ps1 ont alors accès à vos modules réutilisables.



1
votes

Edit: Vous n'avez pas réellement besoin d'utiliser Import-Module. Tant que vous avez les modules dans votre $ env: PSModulePath PowerShell chargera automatiquement toutes les fonctions exportées lors de leur premier appel. Source .

En fonction de les spécificités de votre cas d'utilisation, il existe une autre méthode que vous pouvez utiliser. Cette méthode traite lorsque vous souhaitez importer en masse des modules dans une session PowerShell.

Lorsque vous démarrez PowerShell, elle examine les valeurs de la variable d'environnement $ PSModulePath afin de déterminer où elle doit rechercher les modules. Il recherche ensuite sous ce répertoire les répertoires contenant les fichiers psm1 et psd1. Vous pouvez modifier cette variable pendant la session, puis importer les modules par nom. Voici un exemple, en utilisant ce que j'ai ajouté à mon fichier PowerShell profile.ps1 :

$MyPSPath = [Environment]::GetFolderPath("MyDocuments") + "\WindowsPowerShell"

$env:PSModulePath = $env:PSModulePath + ";$MyPSPath\Custom\Modules"

Import-Module `
    -Name Confirm-Directory, `
    Confirm-File, `
    Get-FileFromURL, `
    Get-RedirectedURL, `
    Get-RemoteFileName, `
    Get-ReparseTarget, `
    Get-ReparseType, `
    Get-SpecialPath, `
    Test-ReparsePoint

Si vous êtes nouveau dans PowerShell profils (ils sont à peu près les mêmes que le fichier ~ / .profile d'Unix), vous pouvez trouver:

  1. plus d'informations sur les profils PowerShell ici .
  2. un résumé des fichiers de profil utilisés et du moment ici .

Bien que cela puisse ne pas sembler aussi pratique qu'un chargeur automatique, l'installation et l'importation de modules est l'approche prévue et acceptée pour cela. Sauf si vous avez une raison spécifique de ne pas le faire, vous devriez essayer de suivre les normes établies afin de ne pas vous battre plus tard pour vous sortir de mauvaises habitudes.

Vous pouvez également modifiez le registre pour y parvenir.

p>


0 commentaires

0
votes

Après quelques recherches, j'ai trouvé: Lors de l'exécution de la fonction LoadModule , toutes les fonctions enregistrées seront ajoutées à Fournisseur de fonctions

Donc, du LoadModule corps de la fonction ils peuvent être énumérés via Get-ChildItem -Path Function:

if($ModuleDevelopment){
    . $PSScriptRoot\..\Shared-Functions\ModuleLoader.ps1 "$PSScriptRoot"
}
else {
    . $PSScriptRoot\Shared\ModuleLoader.ps1 "$PSScriptRoot"
}

Nous pouvons donc stocker la liste des fonctions dans variable au début de l'appel du LoadModule

    $script:functions| `
    where { $_.Name -notlike '_*'  } |  ` # do not extport _Name functions
    %{ Export-ModuleMember -Function $_.Name}

et après la notation de chargement de points, récupère la liste des fonctions ajoutées

    $initScripts = $script:functions| #here{ $_.Name -eq 'Initalize'} #filter
    $initScripts | ForEach-Object{ & $_ } ##execute

Ainsi, la fonction LoadModule modifiée ressemblera à:

$script:functions = LoadModule $script:Private ##Function1.ps1
$script:functions += LoadModule $script:PublicFolder

à l'étape suivante, elle affecte simplement les fonctions à la liste En savoir plus

XXX

Après cela étape, nous pouvons

  • Appeler l'initaliseur:
function LoadModule {
    param ($path)
    $loadRef = Get-PSCallStack

    $loadedFunctions =  Get-ChildItem -Path Function:
    foreach ($import in @($path)) {
        . $import.FullName
    }
    $functions= Get-ChildItem -Path Function: | `
        Where-Object { $loadedFunctions -notcontains $_ } | `
        ForEach-Object{ Get-Item function:$_ }

    return $functions
}
  • et exporter les fonctions publiques:
Get-ChildItem -Path Function: |  where { $loadedFunctions -notcontains $_ } 

Code complet de la fonction de chargement du module J'ai déplacé vers le fichier ModuleLoader.ps1 . Et il peut être trouvé dans le repo GitHub PowershellScripts

Et la version complète du Le fichier Moudule.psm1 est

$loadedFunctions =  Get-ChildItem -Path Function:


0 commentaires