Comment obtenir le répertoire actuel de l'applet de commande en cours d'exécution

202

Cela devrait être une tâche simple, mais j'ai vu plusieurs tentatives pour obtenir le chemin d'accès au répertoire où se trouve l'applet de commande exécutée avec un succès mitigé. Par exemple, lorsque j'exécute C:\temp\myscripts\mycmdlet.ps1un fichier de paramètres, C:\temp\myscripts\settings.xmlj'aimerais pouvoir stocker C:\temp\myscriptsdans une variable à l'intérieur mycmdlet.ps1.

C'est une solution qui fonctionne (bien qu'un peu lourde):

$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$settingspath = $directorypath + '\settings.xml'

Un autre a suggéré cette solution qui ne fonctionne que sur notre environnement de test:

$settingspath = '.\settings.xml'

J'aime beaucoup cette dernière approche et je la préfère avoir à analyser le chemin de fichier comme paramètre à chaque fois, mais je ne peux pas le faire fonctionner sur mon environnement de développement. Que dois-je faire? Cela a-t-il quelque chose à voir avec la configuration de PowerShell?

Stig Perez
la source
11
Notez que le titre ambigu de cette question a permis aux réponses ci-dessous de résoudre l'un des deux problèmes distincts, sans indiquer explicitement lequel: (a) comment référencer l' emplacement actuel (répertoire) ou (b) comment référencer l' emplacement du script en cours d'exécution ( le répertoire dans lequel se trouve le script en cours d'exécution, qui peut être ou non le répertoire en cours).
mklement0

Réponses:

144

La manière fiable de le faire est comme vous l'avez montré $MyInvocation.MyCommand.Path .

L'utilisation de chemins relatifs sera basée sur $ pwd, dans PowerShell, le répertoire actuel pour une application ou le répertoire de travail actuel pour une API .NET.

PowerShell v3 + :

Utilisez la variable automatique $PSScriptRoot.

JasonMArcher
la source
6
Pouvez-vous m'expliquer comment vous avez trouvé la propriété PATH? $ MyInvocation.MyCommand | gm n'affiche pas cette propriété dans la liste des membres.
Vitaliy Markitanov
19
pourquoi ne pas simplement utiliser $ PSScriptRoot? Semble plus fiable
mBrice1024
@ user2326106 Pouvez-vous expliquer la différence entre $PSScriptRootet $MyInvocation.MyCommand.Path?
duct_tape_coder
1
Cette réponse est incomplète.
K - La toxicité du SO augmente.
Cette réponse est incomplète.
stackleit
263

Oui, ça devrait marcher. Mais si vous avez besoin de voir le chemin absolu, c'est tout ce dont vous avez besoin:

(Get-Item .).FullName
GuruKay
la source
4
Merci, c'est une excellente méthode pour trouver le chemin complet à partir des chemins relatifs. Par exemple (Get-Item -Path $ myRelativePath -Verbose) .FullName
dlux
Merci pour ça. Les autres réponses ne fonctionnaient pas pour les scripts Powershell compilés en EXE.
Zach Alexander
10
C'est faux . Cela obtient le répertoire actuel du processus , qui peut être n'importe où. Par exemple, si mon répertoire actuel de ligne de commande est C:\mydiret que j'appelle la commande C:\dir1\dir2\dir3\mycmdlet.ps1, cela se résoudra en C:\mydirnon C:\dir1\dir2\dir3. L'appel d'un nouvel exécutable a le même problème car le répertoire actuel est hérité du processus parent.
jpmc26
85

La méthode la plus simple semble être d'utiliser la variable prédéfinie suivante:

 $PSScriptRoot

about_Automatic_Variableset les about_Scriptsdeux indiquent:

Dans PowerShell 2.0, cette variable n'est valide que dans les modules de script (.psm1). À partir de PowerShell 3.0, il est valide dans tous les scripts.

Je l'utilise comme ceci:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName
Tony Sheen
la source
12
Il est spécifique à la version. Cela nécessite au moins Powershell 3.0.
Marvin Dickhaus
1
C'est ce dont j'avais besoin pour référencer un fichier au même endroit que le script - merci!
Adam Prescott
C'est la meilleure réponse car elle vous donne précisément le chemin où votre script PS est présent, qui est essentiellement la racine de l'exécution de votre script. Peu importe quel est votre répertoire de travail actuel d'où vous avez appelé vos scripts. +1.
RBT
@MarvinDickhaus C'est pourquoi il est nécessaire d'utiliser "Set-StrictMode -Version 3.0" dans la plupart de vos scripts :) Un grand merci pour les liens!
Alexander Shapkin
44

Vous pouvez aussi utiliser:

(Resolve-Path .\).Path

La partie entre parenthèses renvoie un PathInfo objet.

(Disponible depuis PowerShell 2.0.)

Alex Angas
la source
2
C'est faux . Cela obtient le répertoire actuel du processus , qui peut être n'importe où. Par exemple, si mon répertoire actuel de ligne de commande est C:\mydiret que j'appelle la commande C:\dir1\dir2\dir3\mycmdlet.ps1, cela se résoudra en C:\mydirnon C:\dir1\dir2\dir3. L'appel d'un nouvel exécutable a le même problème car le répertoire actuel est hérité du processus parent.
jpmc26
3
Merci! J'ai également mal compris le titre de cette question, et cette réponse était exactement ce que je cherchais. Cependant ... Cela ne répond pas à la question.
Ryan The Leach
33

Le chemin est souvent nul. Cette fonction est plus sûre.

function Get-ScriptDirectory
{
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value;
    if($Invocation.PSScriptRoot)
    {
        $Invocation.PSScriptRoot;
    }
    Elseif($Invocation.MyCommand.Path)
    {
        Split-Path $Invocation.MyCommand.Path
    }
    else
    {
        $Invocation.InvocationName.Substring(0,$Invocation.InvocationName.LastIndexOf("\"));
    }
}
Christian Flem
la source
1
pourquoi -Scope 1? pas -Scope 0
suiwenfeng
1
Get-Variable: le numéro de portée «1» dépasse le nombre de portées actives.
suiwenfeng
2
Vous obtenez cette erreur car vous n'avez pas de portée parent. Le paramètre -Scope obtient la variable dans une étendue spécifiée. 1 dans ce cas est la portée parent. Pour plus d'informations, consultez cet article technet sur Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Christian Flem
27

Essayez:

(Get-Location).path

ou:

($pwd).path
Rohin Sidharth
la source
J'oublie toujours $pwd/ $PWDchaque fois que je fais une longue pause avec PowerShell! Tellement plus utile IMO ...
kayleeFrye_onDeck
17

Get-Location renverra l'emplacement actuel:

$Currentlocation = Get-Location
Veera Induvasi
la source
3
PS C: \ Windows \ system32> C: \ powershell \ checkfile.ps1 -> cela donnera c: \ windows \ system32
nbi
11

J'aime la solution monoligne :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
FrankyHollywood
la source
C'est la meilleure solution jusqu'à présent.
Teoman shipahi
Oui, ça marche pour moi. Mon pwd est mon répertoire utilisateur et j'exécute le script dans un autre répertoire (B) qu'il donne (B)
andrew pate
8

Essaye ça:

$WorkingDir = Convert-Path .
Sevenfold Exurbanite
la source
4

Dans Powershell 3 et supérieur, vous pouvez simplement utiliser

$PSScriptRoot

BlackSpy
la source
1

On pourrait penser que l'utilisation de '. \' Comme chemin signifie que c'est le chemin d'invocation. Mais pas tout le temps. Exemple, si vous l'utilisez dans un Job ScriptBlock. Dans ce cas, il peut pointer vers% profile% \ Documents.

syanzy
la source
1

cette fonction définira l'emplacement de l'invite sur le chemin du script, traitant de la manière différente d'obtenir le chemin du script entre vscode, psise et pwd:

function Set-CurrentLocation
{
    $currentPath = $PSScriptRoot                                                                                                     # AzureDevOps, Powershell
    if (!$currentPath) { $currentPath = Split-Path $pseditor.GetEditorContext().CurrentFile.Path -ErrorAction SilentlyContinue }     # VSCode
    if (!$currentPath) { $currentPath = Split-Path $psISE.CurrentFile.FullPath -ErrorAction SilentlyContinue }                       # PsISE

    if ($currentPath) { Set-Location $currentPath }
}
freakydinde
la source
1

La plupart des réponses ne fonctionnent pas lors du débogage dans les IDE suivants:

  • PS-ISE (PowerShell ISE)
  • Code VS (code Visual Studio)

Parce que dans ceux-ci, le $PSScriptRootest vide et Resolve-Path .\(et similaires) entraînera des chemins incorrects.

Réponse de Freakydinde est la seule qui résout ces situations, alors j'ai voté contre, mais je ne pense pas que la Set-Locationréponse soit vraiment ce que l'on souhaite. J'ai donc corrigé cela et rendu le code un peu plus clair:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { split-path $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }
Mariano Desanze
la source
0

Pour ce que ça vaut, pour être une solution monoligne, ce qui suit est une solution de travail pour moi.

$currFolderName = (Get-Location).Path.Substring((Get-Location).Path.LastIndexOf("\")+1)

Le 1 à la fin est d'ignorer le / .

Merci aux articles ci-dessus en utilisant l' applet de commande Get-Location .

Ak777
la source
-1

Pour développer la réponse de @Cradle: vous pouvez également écrire une fonction polyvalente qui vous donnera le même résultat selon la question de l'OP:

Function Get-AbsolutePath {

    [CmdletBinding()]
    Param(
        [parameter(
            Mandatory=$false,
            ValueFromPipeline=$true
        )]
        [String]$relativePath=".\"
    )

    if (Test-Path -Path $relativePath) {
        return (Get-Item -Path $relativePath).FullName -replace "\\$", ""
    } else {
        Write-Error -Message "'$relativePath' is not a valid path" -ErrorId 1 -ErrorAction Stop
    }

}
Erutan409
la source
-1

Si vous avez juste besoin du nom du répertoire courant, vous pouvez faire quelque chose comme ceci:

((Get-Location) | Get-Item).Name

En supposant que vous travaillez à partir de C: \ Temp \ Location \ MyWorkingDirectory>

Production

MyWorkingDirectory

SolThoth
la source
-1

J'ai eu des problèmes similaires et cela m'a posé beaucoup de problèmes car je crée des programmes écrits en PowerShell (applications GUI utilisateur final) et j'ai beaucoup de fichiers et de ressources à charger à partir du disque. D'après mon expérience, en utilisant . Après cela, PowerShell change de répertoire soit en répertoire à partir duquel vous l'avez appelé, soit en répertoire où se trouve le script que vous exécutez avant de vous présenter l'invite PowerShell ou d'exécuter le script. Mais cela se produit une fois que l'application PowerShell elle-même a commencé à l'origine dans votre répertoire utilisateur personnel.. pour représenter le répertoire actuel n'est pas fiable. Il devrait représenter le répertoire de travail actuel, mais ce n'est souvent pas le cas. Il semble que PowerShell enregistre l'emplacement à partir duquel PowerShell a été appelé à l'intérieur .. Pour être plus précis, lorsque PowerShell est démarré pour la première fois, il démarre, par défaut, dans votre répertoire utilisateur personnel. Il s'agit généralement du répertoire de votre compte utilisateur, quelque chose commeC:\USERS\YOUR USER NAME

Et .représente le répertoire initial dans lequel PowerShell a démarré. .Représente donc uniquement le répertoire courant au cas où vous auriez appelé PowerShell à partir du répertoire voulu. Si vous modifiez ultérieurement le répertoire dans le code PowerShell, le changement ne semble pas être reflété à l'intérieur .dans tous les cas. Dans certains cas, .représente le répertoire de travail actuel et dans d'autres, le répertoire à partir duquel PowerShell (lui-même et non le script) a été appelé, ce qui peut conduire à des résultats incohérents. Pour cette raison, j'utilise un script d'invocateur. De script PowerShell avec commande unique à l' intérieur: POWERSHELL. Cela garantira que PowerShell est invoqué à partir du répertoire voulu et fera ainsi.représente le répertoire courant. Mais cela ne fonctionne que si vous ne changez pas de répertoire ultérieurement dans le code PowerShell. Dans le cas d'un script, j'utilise script invocateur qui est semblable à dernier je l' ai mentionné, sauf qu'il contient une option de fichier: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Cela garantit que PowerShell est démarré dans le répertoire de travail actuel.

Un simple clic sur le script appelle PowerShell à partir de votre répertoire d’utilisateurs personnels, quel que soit l’emplacement du script. Il en résulte que le répertoire de travail actuel est le répertoire où se trouve le script, mais que le répertoire d'invocation PowerShell est C:\USERS\YOUR USER NAME, et avec le .retour de l'un de ces deux répertoires en fonction de la situation, ce qui est ridicule.

Mais pour éviter toute cette agitation et utiliser le script invocateur, vous pouvez simplement utiliser soit $PWDou $PSSCRIPTROOTau lieu de .pour représenter le répertoire actuel en fonction de la météo que vous souhaitez représenter le répertoire de travail actuel ou le répertoire à partir duquel le script a été invoqué. Et si vous, pour une raison quelconque, souhaitez récupérer un autre des deux répertoires qui .retourne, vous pouvez utiliser $HOME.

Personnellement, je n'ai qu'un script d'invocateur dans le répertoire racine de mes applications que je développe avec PowerShell qui invoque mon script d'application principal, et n'oubliez pas de ne jamais changer le répertoire de travail actuel dans le code source de mon application, donc je n'ai jamais à m'en soucier, et je peux utiliser .pour représenter le répertoire actuel et prendre en charge l'adressage de fichiers relatif dans mes applications sans aucun problème. Cela devrait fonctionner dans les versions plus récentes de PowerShell (plus récentes que la version 2).

SYOB SYOT
la source