web-dev-qa-db-fra.com

Comment puis-je trouver le chemin source d'un script en cours d'exécution?

Je veux pouvoir dire depuis quel chemin mon script d'exécution a été exécuté.
Ce ne sera souvent pas $ pwd.

J'ai besoin d'appeler d'autres scripts qui sont dans une structure de dossiers relative à mon script et bien que je puisse coder en dur les chemins, c'est à la fois désagréable et un peu pénible lorsque j'essaye de passer de "dev" à "test" à "production".

60
Kevin Buchan

Le script omniprésent à l'origine publié par Jeffrey Snover de l'équipe PowerShell (donné dans réponse de Skyler ) et les variations publiées par Keith Cedirc et EBGreen, souffrent tous d'un grave inconvénient- - si le code rapporte ce que vous attendez dépend de l'endroit où vous l'appelez !

Mon code ci-dessous résout ce problème en référençant simplement le script la portée au lieu de le parent portée:

function Get-ScriptDirectory
{
    Split-Path $script:MyInvocation.MyCommand.Path
}

Pour illustrer le problème, j'ai créé un véhicule de test qui évalue l'expression cible de quatre manières différentes. (Les termes entre crochets sont les clés du tableau de résultats suivant.)

  1. code en ligne [en ligne]
  2. fonction inline, c'est-à-dire fonction dans le programme principal [fonction inline]
  3. Fonction basée sur des points, c'est-à-dire que la même fonction a été déplacée dans un fichier .ps1 distinct [source de points]
  4. Fonction module, c'est-à-dire que la même fonction a été déplacée dans un fichier .psm1 séparé [module]

Les deux dernières colonnes montrent le résultat de l'utilisation de la portée du script (c'est-à-dire $ script :) ou de la portée parent (avec -scope 1). Un résultat de "script" signifie que l'invocation a correctement signalé l'emplacement du script. Le résultat "module" signifie que l'invocation a signalé l'emplacement du module contenant la fonction plutôt que le script qui a appelé la fonction; cela indique un inconvénient des deux fonctions que vous ne pouvez pas mettre la fonction dans un module.

En mettant de côté le problème du module, l'observation remarquable du tableau est que l'utilisation de l'approche de portée parent échoue la plupart du temps (en fait, deux fois plus souvent réussit).

table of input combinations

Enfin, voici le véhicule d'essai:

function DoubleNested()
{
    "=== DOUBLE NESTED ==="
    NestCall
}

function NestCall()
{
    "=== NESTED ==="
    "top level:"
    Split-Path $script:MyInvocation.MyCommand.Path
    #$foo = (Get-Variable MyInvocation -Scope 1).Value
    #Split-Path $foo.MyCommand.Path
    "immediate func call"
    Get-ScriptDirectory1
    "dot-source call"
    Get-ScriptDirectory2
    "module call"
    Get-ScriptDirectory3
}

function Get-ScriptDirectory1
{
    Split-Path $script:MyInvocation.MyCommand.Path
    # $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    # Split-Path $Invocation.MyCommand.Path
}

. .\ScriptDirFinder.ps1
Import-Module ScriptDirFinder -force

"top level:"
Split-Path $script:MyInvocation.MyCommand.Path
#$foo = (Get-Variable MyInvocation -Scope 1).Value
#Split-Path $foo.MyCommand.Path

"immediate func call"
Get-ScriptDirectory1
"dot-source call"
Get-ScriptDirectory2
"module call"
Get-ScriptDirectory3

NestCall
DoubleNested

Contenu de ScriptDirFinder.ps1:

function Get-ScriptDirectory2
{
    Split-Path $script:MyInvocation.MyCommand.Path
#   $Invocation = (Get-Variable MyInvocation -Scope 1).Value
#   Split-Path $Invocation.MyCommand.Path
}

Contenu de ScriptDirFinder.psm1:

function Get-ScriptDirectory3
{
    Split-Path $script:MyInvocation.MyCommand.Path
#   $Invocation = (Get-Variable MyInvocation -Scope 1).Value
#   Split-Path $Invocation.MyCommand.Path
}

Je ne connais pas ce qui a été introduit dans PowerShell 2, mais il se pourrait très bien que la portée du script n'existait pas dans PowerShell 1, au moment où Jeffrey Snover a publié son exemple.

J'ai été surpris quand, même si j'ai trouvé son exemple de code proliféré partout sur le Web, il a échoué immédiatement quand je l'ai essayé! Mais c'est parce que je l'ai utilisé différemment de l'exemple de Snover (je l'ai appelé non pas en haut du script mais à l'intérieur d'une autre fonction (mon exemple "imbriqué deux fois").)

Mise à jour 2011.09.12

Vous pouvez lire à ce sujet avec d'autres conseils et astuces sur les modules dans mon article qui vient d'être publié sur Simple-Talk.com: Plus loin dans le trou du lapin: modules PowerShell et encapsulation .

96
Michael Sorens

Vous avez marqué votre question pour Powershell version 1.0, cependant, si vous avez accès à Powershell version 3.0, vous savez que vous avez $PSCommandPathet$PSScriptRootqui facilite l'obtention du chemin du script. Veuillez vous référer à la section "AUTRES FONCTIONS SCRIPT" sur cette page pour plus d'informations.

20
Michael Kelley

Nous utilisons du code comme celui-ci dans la plupart de nos scripts depuis plusieurs années sans aucun problème:

#--------------------------------------------------------------------
# Dot source support scripts
#--------------------------------------------------------------------
$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir  = Split-Path -Parent $ScriptPath
. $ScriptDir\BuildVars.ps1
. $ScriptDir\LibraryBuildUtils.ps1
. $ScriptDir\BuildReportUtils.ps1
11
Keith Hill

J'ai rencontré le même problème récemment. L'article suivant m'a aidé à résoudre le problème: http://blogs.msdn.com/powershell/archive/2007/06/19/get-scriptdirectory.aspx

Si vous n'êtes pas intéressé par son fonctionnement, voici tout le code dont vous avez besoin dans l'article:

function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}

Et puis vous obtenez le chemin en faisant simplement:

$path = Get-ScriptDirectory
6
Skyler

Je pense que vous pouvez trouver le chemin de votre script en cours en utilisant

$MyInvocation.MyCommand.Path

J'espère que ça aide !

Cédric

5
Cédric Rup

C'est une de ces bizarreries (à mon avis du moins) dans PS. Je suis sûr qu'il y a une très bonne raison à cela, mais cela me semble toujours étrange. Alors:

Si vous êtes dans un script mais pas dans une fonction, alors $ myInvocation.InvocationName vous donnera le chemin complet, y compris le nom du script. Si vous êtes dans un script et à l'intérieur d'une fonction, alors $ myInvocation.ScriptName vous donnera la même chose.

1
EBGreen

Merci msorens! Cela m'a vraiment aidé avec mon module personnalisé. Au cas où quelqu'un souhaiterait créer le sien, voici comment le mien est structuré.

MyModule (folder)
 - MyModule.psd1 (help New-ModuleManifest)
 - MyScriptFile.ps1 (ps1 files are easy to test)

Vous référencez ensuite MyScriptFile.ps1 dans MyModule.psd1. Le fait de référencer le .ps1 dans le tableau NestedModules place les fonctions dans l'état de session du module plutôt que dans l'état de session global. ( Comment écrire un manifeste de module )

NestedModules = @('.\MyScriptFile.ps1','.\MyOtherScriptFile.ps1')

Contenu de MyScriptFile.ps1

function Get-ScriptDirectory {
    Split-Path $script:MyInvocation.MyCommand.Path
}

try {
    Export-ModuleMember -Function "*-*"
}
catch{}

Le try/catch masque l'erreur d'Export-ModuleMember lors de l'exécution de MyScriptFile.ps1

Copiez le répertoire MyModule dans l'un des chemins trouvés ici $ env: PSModulePath

PS C:\>Import-Module MyModule
PS C:\>Get-Command -Module MyModule

CommandType     Name                                               ModuleName                                                                                                                                                
-----------     ----                                               ----------                                                                                                                                                
Function        Get-ScriptDirectory                                MyModule  
0
Coding101