Comment puis-je obtenir le fichier d'exécution actuel de PowerShell?

92

Remarque: PowerShell 1.0
Je voudrais obtenir le nom du fichier PowerShell en cours d'exécution. Autrement dit, si je commence ma session comme ceci:

powershell.exe .\myfile.ps1

Je voudrais obtenir la chaîne ". \ Myfile.ps1" (ou quelque chose comme ça). EDIT : "myfile.ps1" est préférable.
Des idées?

Ron Klein
la source
Merci, les réponses actuelles sont presque les mêmes, mais je n'ai besoin que du nom du fichier (et non du chemin complet), donc la réponse acceptée est @ Keith. +1 pour les deux réponses, cependant. Maintenant, je connais le truc $ MyInvocation :-)
Ron Klein
Que diriez-vous d'obtenir le script parent à partir d'un script inclus?
Florin Sabau

Réponses:

67

J'ai essayé de résumer les différentes réponses ici, mises à jour pour PowerShell 5:

  • Si vous utilisez uniquement PowerShell 3 ou version ultérieure, utilisez $PSCommandPath

  • Si vous souhaitez une compatibilité avec les anciennes versions, insérez la cale:

    if ($PSCommandPath -eq $null) { function GetPSCommandPath() { return $MyInvocation.PSCommandPath; } $PSCommandPath = GetPSCommandPath; }

    Cela ajoute $PSCommandPaths'il n'existe pas déjà.

    Le code de shim peut être exécuté n'importe où (au niveau supérieur ou à l'intérieur d'une fonction), bien que la $PSCommandPathvariable soit soumise à des règles de portée normales (par exemple, si vous mettez le shim dans une fonction, la variable est portée à cette fonction uniquement).

Détails

Il y a 4 méthodes différentes utilisées dans diverses réponses, j'ai donc écrit ce script pour démontrer chacune (plus $PSCommandPath):

function PSCommandPath() { return $PSCommandPath; }
function ScriptName() { return $MyInvocation.ScriptName; }
function MyCommandName() { return $MyInvocation.MyCommand.Name; }
function MyCommandDefinition() {
    # Begin of MyCommandDefinition()
    # Note: ouput of this script shows the contents of this function, not the execution result
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()
}
function MyInvocationPSCommandPath() { return $MyInvocation.PSCommandPath; }

Write-Host "";
Write-Host "PSVersion: $($PSVersionTable.PSVersion)";
Write-Host "";
Write-Host "`$PSCommandPath:";
Write-Host " *   Direct: $PSCommandPath";
Write-Host " * Function: $(ScriptName)";
Write-Host "";
Write-Host "`$MyInvocation.ScriptName:";
Write-Host " *   Direct: $($MyInvocation.ScriptName)";
Write-Host " * Function: $(ScriptName)";
Write-Host "";
Write-Host "`$MyInvocation.MyCommand.Name:";
Write-Host " *   Direct: $($MyInvocation.MyCommand.Name)";
Write-Host " * Function: $(MyCommandName)";
Write-Host "";
Write-Host "`$MyInvocation.MyCommand.Definition:";
Write-Host " *   Direct: $($MyInvocation.MyCommand.Definition)";
Write-Host " * Function: $(MyCommandDefinition)";
Write-Host "";
Write-Host "`$MyInvocation.PSCommandPath:";
Write-Host " *   Direct: $($MyInvocation.PSCommandPath)";
Write-Host " * Function: $(MyInvocationPSCommandPath)";
Write-Host "";

Production:

PS C:\> .\Test\test.ps1

PSVersion: 5.1.19035.1

$PSCommandPath:
 *   Direct: C:\Test\test.ps1
 * Function: C:\Test\test.ps1

$MyInvocation.ScriptName:
 *   Direct:
 * Function: C:\Test\test.ps1

$MyInvocation.MyCommand.Name:
 *   Direct: test.ps1
 * Function: MyCommandName

$MyInvocation.MyCommand.Definition:
 *   Direct: C:\Test\test.ps1
 * Function:
    # Begin of MyCommandDefinition()
    # Note this is the contents of the MyCommandDefinition() function, not the execution results
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()


$MyInvocation.PSCommandPath:
 *   Direct:
 * Function: C:\Test\test.ps1

Remarques:

  • Exécuté à partir de C:\, mais le script réel l'est C:\Test\test.ps1.
  • Aucune méthode ne vous indique le chemin d'appel passé ( .\Test\test.ps1)
  • $PSCommandPath est le seul moyen fiable, mais a été introduit dans PowerShell 3
  • Pour les versions antérieures à 3, aucune méthode ne fonctionne à la fois à l'intérieur et à l'extérieur d'une fonction
Gregmac
la source
7
Pour tous ceux qui lisent aujourd'hui (2017), ils devraient lire CE message comme la bonne réponse! +1
Collin Chaffin
2
@CollinChaffin: d'accord et maintenant (2017) le moins pris en charge actuellement est Windows 7, il n'y a donc aucune raison de ne pas l'utiliser $PSCommandPathsi l'héritage (WindowsXP) n'est pas requis.
tukan du
Le premier exemple de code est imparfait car il contient deux définitions de la même fonction ( function PSCommandPath) et une référence à la mauvaise fonction ( Write-Host " * Direct: $PSCommandPath"; Write-Host " * Function: $(ScriptName)";- ou est-ce que je néglige quelque chose d'évident?
Mike L'Angelo
@ MikeL'Angelo Vous avez raison! Je suis passé inaperçu pendant 3 ans. Fixé, merci. Le résultat et la conclusion sont les mêmes.
gregmac
81

Bien que la réponse actuelle soit correcte dans la plupart des cas, dans certaines situations, elle ne vous donnera pas la bonne réponse. Si vous utilisez dans vos fonctions de script, alors:

$MyInvocation.MyCommand.Name 

Renvoie le nom de la fonction au lieu du nom du nom du script.

function test {
    $MyInvocation.MyCommand.Name
}

Vous donnera un " test " quel que soit le nom de votre script. La bonne commande pour obtenir le nom du script est toujours

$MyInvocation.ScriptName

cela renvoie le chemin complet du script que vous exécutez. Si vous n'avez besoin que du nom de fichier du script, ce code devrait vous aider:

split-path $MyInvocation.PSCommandPath -Leaf
Lukas Kucera
la source
6
Notez qu'au niveau supérieur, Scriptname n'est pas défini avec posh v4. J'aime utiliser au niveau supérieur, $ MyInvocation.MyCommand.Definition pour le chemin complet ou le nom selon les autres réponses.
AnneTheAgile
30
$MyInvocation.ScriptNamerenvoie une chaîne vide pour moi, PS v3.0.
JohnC
4
@JohnC $MyInvocation.ScriptNamene fonctionne que depuis l'intérieur d'une fonction. Voir ma réponse ci-dessous .
gregmac
72

Si vous ne voulez que le nom du fichier (pas le chemin complet), utilisez ceci:

$ScriptName = $MyInvocation.MyCommand.Name
Keith Hill
la source
32

Essayez ce qui suit

$path =  $MyInvocation.MyCommand.Definition 

Cela peut ne pas vous donner le chemin réel saisi mais cela vous donnera un chemin valide vers le fichier.

JaredPar
la source
1
@Hamish la question dit spécifiquement si elle est invoquée à partir d'un fichier.
JaredPar
FYI: Cela vous donne le chemin complet et le nom du fichier (Powershell 2.0)
Ralph Willgoss
Je cherchais exactement cette commande. Merci, JaredPar! :)
sqlfool
Utilisez Split-Path pour obtenir le répertoire? $path = Split-Path $MyInvocation.MyCommand.Definition -Parent
Underverse
7

Si vous recherchez le répertoire courant dans lequel le script est exécuté, vous pouvez essayer celui-ci:

$fullPathIncFileName = $MyInvocation.MyCommand.Definition
$currentScriptName = $MyInvocation.MyCommand.Name
$currentExecutingPath = $fullPathIncFileName.Replace($currentScriptName, "")

Write-Host $currentExecutingPath
Ryk
la source
1
Cela ne fonctionnerait pas correctement C:\ilike.ps123\ke.ps1, n'est-ce pas?
fridojet
@fridojet - Pas sûr, pas près d'un terminal PS pour le tester. Pourquoi ne pas l'essayer et voir?
Ryk
Non, juste une question rhétorique ;-) - Ce serait juste logique car la Replace()méthode remplace chaque occurrence de l'aiguille (pas seulement la dernière occurrence) et je l'ai également testée. Cependant, c'est une bonne idée de faire quelque chose comme la soustraction sur des chaînes.
fridojet
... Et String.TrimEnd()( $currentExecutingPath = $fullPathIncFileName.TrimEnd($currentScriptName))? - Cela fonctionne correctement: "Ich bin Hamster".TrimEnd("ster")retourne Ich bin Hamet "Ich bin Hamsterchen".TrimEnd("ster")retourne Ich bin Hamsterchen(au lieu de Ich bin Hamchen) - Très bien!
fridojet
$currentScriptPath = $MyInvocation.MyCommand.Definition; $currentScriptName = $MyInvocation.MyCommand.Name; $currentScriptDir = $currentScriptPath.Substring(0,$currentScriptPath.IndexOf($currentScriptName));
PJ
7

attention: contrairement aux variables automatiques $PSScriptRootet $PSCommandPath, les propriétés PSScriptRootet PSCommandPathde la $MyInvocationvariable automatique contiennent des informations sur l'appelant ou le script appelant, et non sur le script courant.

par exemple

PS C:\Users\S_ms\OneDrive\Documents> C:\Users\SP_ms\OneDrive\Documents\DPM ...
=!C:\Users\S_ms\OneDrive\Documents\DPM.ps1

... où DPM.ps1contient

Write-Host ("="+($MyInvocation.PSCommandPath)+"!"+$PSCommandPath)
MWR
la source
4

Je dirais qu'il existe une meilleure méthode, en définissant la portée de la variable $ MyInvocation.MyCommand.Path:

ex> $ script : MyInvocation.MyCommand.Name

Cette méthode fonctionne dans toutes les circonstances d'invocation:

EX: Somescript.ps1

function printme () {
    "In function:"
    ( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
    ( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
    ( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
}
"Main:"
( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
" "
printme
exit

PRODUCTION:

PS> powershell C:\temp\test.ps1
Main:
MyInvocation.ScriptName:
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: test.ps1

In function:
MyInvocation.ScriptName: C:\temp\test.ps1
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: printme

Notez que la réponse acceptée ci-dessus ne renvoie PAS de valeur lorsqu'elle est appelée depuis Main. Notez également que la réponse acceptée ci-dessus renvoie le chemin complet lorsque la question a demandé le nom du script uniquement. La variable de portée fonctionne dans tous les endroits.

De plus, si vous vouliez le chemin complet, vous appelleriez simplement:

$script:MyInvocation.MyCommand.Path
Papa
la source
3

Comme indiqué dans les réponses précédentes, l'utilisation de "$ MyInvocation" est sujette à des problèmes de portée et ne fournit pas nécessairement des données cohérentes (valeur de retour par rapport à la valeur d'accès direct). J'ai trouvé que la méthode la plus "propre" (la plus cohérente) pour obtenir des informations de script comme le chemin du script, le nom, les paramètres, la ligne de commande, etc. quelle que soit la portée (dans les appels de fonction principaux ou ultérieurs / imbriqués) consiste à utiliser "Get- Variable "sur" MyInvocation "...

# Get the MyInvocation variable at script level
# Can be done anywhere within a script
$ScriptInvocation = (Get-Variable MyInvocation -Scope Script).Value

# Get the full path to the script
$ScriptPath = $ScriptInvocation.MyCommand.Path

# Get the directory of the script
$ScriptDirectory = Split-Path $ScriptPath

# Get the script name
# Yes, could get via Split-Path, but this is "simpler" since this is the default return value
$ScriptName = $ScriptInvocation.MyCommand.Name

# Get the invocation path (relative to $PWD)
# @GregMac, this addresses your second point
$InvocationPath = ScriptInvocation.InvocationName

Ainsi, vous pouvez obtenir les mêmes informations que $ PSCommandPath, mais beaucoup plus dans l'accord. Pas sûr, mais il semble que "Get-Variable" n'était pas disponible avant la PS3 donc pas beaucoup d'aide pour les systèmes vraiment anciens (non mis à jour).

Il y a aussi des aspects intéressants lors de l'utilisation de "-Scope" car vous pouvez revenir en arrière pour obtenir les noms, etc. de la ou des fonctions appelantes. 0 = actuel, 1 = parent, etc.

J'espère que cela est quelque peu utile.

Réf, https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-variable

AntGut
la source
1

J'ai fait quelques tests avec le script suivant, sur PS 2 et PS 4 et a eu le même résultat. J'espère que cela aide les gens.

$PSVersionTable.PSVersion
function PSscript {
  $PSscript = Get-Item $MyInvocation.ScriptName
  Return $PSscript
}
""
$PSscript = PSscript
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

""
$PSscript = Get-Item $MyInvocation.InvocationName
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

Résultats -

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      -1     -1      

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts
Mark Crashley
la source
1

Cela peut fonctionner sur la plupart des versions PowerShell:

(& { $MyInvocation.ScriptName; })

Cela peut fonctionner pour le travail planifié

Get-ScheduledJob |? Name -Match 'JOBNAMETAG' |% Command
improbable
la source