PowerShell: exécuter la commande à partir du répertoire du script

144

J'ai un script PowerShell qui fait des choses en utilisant le répertoire actuel du script. Ainsi, à l'intérieur de ce répertoire, l'exécution .\script.ps1fonctionne correctement.

Maintenant, je veux appeler ce script à partir d'un répertoire différent sans changer le répertoire de référence du script. Je veux donc appeler ..\..\dir\script.ps1et je veux toujours que ce script se comporte comme il a été appelé depuis son répertoire.

Comment faire cela ou comment modifier un script pour qu'il puisse s'exécuter à partir de n'importe quel répertoire?

poussée
la source

Réponses:

200

Voulez-vous dire que vous voulez le chemin du script afin de pouvoir référencer un fichier à côté du script? Essaye ça:

$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
Write-host "My directory is $dir"

Vous pouvez obtenir beaucoup d'informations à partir de $ MyInvocation et de ses propriétés.

Si vous souhaitez référencer un fichier dans le répertoire de travail actuel, vous pouvez utiliser Resolve-Path ou Get-ChildItem:

$filepath = Resolve-Path "somefile.txt"

EDIT (basé sur le commentaire de OP):

# temporarily change to the correct folder
Push-Location $folder

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Il existe probablement d'autres moyens de réaliser quelque chose de similaire en utilisant Invoke-Command.

JohnL
la source
1
Hmm, d'accord, j'aurais peut-être dû être un peu plus clair sur mon scénario. En fait, c'est un script qui appelle antavec certains paramètres. Je dois donc appeler à antpartir de ce dossier pour m'assurer qu'il trouve correctement le fichier de configuration. Idéalement, je cherche quelque chose pour changer temporairement le répertoire d'exécution localement dans ce script.
poke le
3
Ah alors dans ce cas, vous voudrez Push-LocationetPop-Location
JohnL
5
attention à ce sujet, $ MyInvocation est sensible au contexte, je fais généralement $ ScriptPath = (Get-Variable MyInvocation -Scope Script) .Value.MyCommand.Path qui fonctionne à partir de toute sorte d'imbrication ou de fonction
39

Si vous appelez des applications natives, vous devez vous soucier [Environment]::CurrentDirectorydu $PWDrépertoire actuel de PowerShell . Pour diverses raisons, PowerShell ne définit pas le répertoire de travail actuel du processus lorsque vous définissez un emplacement ou un emplacement push, vous devez donc vous assurer de le faire si vous exécutez des applications (ou applets de commande) qui s'attendent à ce qu'il soit défini.

Dans un script, vous pouvez faire ceci:

$CWD = [Environment]::CurrentDirectory

Push-Location $MyInvocation.MyCommand.Path
[Environment]::CurrentDirectory = $PWD
##  Your script code calling a native executable
Pop-Location

# Consider whether you really want to set it back:
# What if another runspace has set it in-between calls?
[Environment]::CurrentDirectory = $CWD

Il n'y a pas d'alternative infaillible à cela. Beaucoup d'entre nous mettent une ligne dans notre fonction d'invite pour définir [Environment] :: CurrentDirectory ... mais cela ne vous aide pas lorsque vous changez l'emplacement dans un script.

Deux remarques sur la raison pour laquelle cela n'est pas défini automatiquement par PowerShell:

  1. PowerShell peut être multithread. Vous pouvez avoir plusieurs Runspaces (voir RunspacePool et le module PSThreadJob) s'exécutant simultanément au sein d'un même processus. Chaque espace d'exécution a son propre $PWDrépertoire de travail, mais il n'y a qu'un seul processus et un seul environnement.
  2. Même lorsque vous êtes à un seul thread, ce $PWDn'est pas toujours un CurrentDirectory légal (vous pouvez CD dans le fournisseur de registre par exemple).

Si vous souhaitez le placer dans votre invite (qui ne fonctionnerait que dans l'espace d'exécution principal, à un seul thread), vous devez utiliser:

[Environment]::CurrentDirectory = Get-Location -PSProvider FileSystem
Jaykul
la source
4
Cela ne change pas le répertoire de travail du processus à cause des fournisseurs de chemin: vous pourriez être en train de copier un CD dans le registre, une base de données SQL ou une ruche IIS, etc.
piers7
1
Oui. Je sais, c'était le but de cette dernière "note finale". Ils auraient pu (comme je le fais dans ma fonction d'invite) le mettre à jour à l'emplacement actuel du fournisseur FileSystem, comme tous les autres shell dans le monde.
Jaykul
2
Saints dieux du script, je suis tellement déçu .\_/.- pour cela a tué la moitié de ma journée! Les gens, sérieusement? Sérieusement? ..
ulidtko
2
C'est pratiquement inutile maintenant, les versions plus modernes de PowerShell définissent le répertoire de travail lorsqu'elles lancent des applications binaires.
Jaykul
1
Cette méthode échoue à restaurer l'original [Environment]::CurrentDirectorylorsque cela diffère de $PWD. La bonne chose à faire serait de la stocker dans une variable $origEnvDir = [Environment]::CurrentDirectory, puis de la restaurer ultérieurement [Environment]::CurrentDirectory = $origEnvDir.
Strom
37

Il y a des réponses avec un grand nombre de votes, mais quand j'ai lu votre question, j'ai pensé que vous vouliez connaître le répertoire où se trouve le script, pas celui où le script s'exécute. Vous pouvez obtenir les informations avec les variables automatiques de PowerShell

$PSScriptRoot - the directory where the script exists, not the target directory the script is running in
$PSCommandPath - the full path of the script

Par exemple, j'ai un script $ profile qui trouve le fichier de solution de Visual Studio et le démarre. Je voulais stocker le chemin complet, une fois qu'un fichier de solution est démarré. Mais je voulais enregistrer le fichier où le script d'origine existe. J'ai donc utilisé $ PsScriptRoot.

Andy
la source
12

J'ai souvent utilisé le code suivant pour importer un module qui se trouve dans le même répertoire que le script en cours d'exécution. Il obtiendra d'abord le répertoire à partir duquel PowerShell s'exécute

$ currentPath = Split-Path ((Get-Variable MyInvocation -Scope 0) .Value) .MyCommand.Path

module d'importation "$ currentPath \ sqlps.ps1"

Daniel Wu
la source
vous voulez définir le -Scope sur Script
9

Cela fonctionnerait très bien.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir
ArunSK
la source
N'oubliez pas d'appeler Pop-Locationà la fin pour ramener le chemin à son état d'origine.
Lam Le
2

Eh bien, je cherchais une solution pour cela pendant un moment, sans aucun script uniquement de CLI. Voici comment je fais xD:

  • Accédez au dossier à partir duquel vous souhaitez exécuter le script (l'important est que vous ayez des onglets complets)

    ..\..\dir

  • Maintenant, entourez l'emplacement avec des guillemets doubles et ajoutez-y à l'intérieur cd, afin que nous puissions appeler une autre instance de PowerShell.

    "cd ..\..\dir"

  • Ajoutez une autre commande pour exécuter le script séparée par ;, avec un séparateur de commande dans PowerShell

    "cd ..\..\dir\; script.ps1"

  • Enfin, exécutez-le avec une autre instance de PowerShell

    start powershell "cd..\..\dir\; script.ps1"

Cela va ouvrir une nouvelle fenêtre PowerShell, aller à ..\..\dir, exécuter script.ps1et fermer la fenêtre.


Notez que ";" sépare simplement les commandes, comme vous les avez tapées une par une, si la première échoue, la seconde s'exécutera et la suivante après, et la suivante après ... Si vous voulez garder une nouvelle fenêtre PowerShell ouverte, ajoutez -noexit dans la commande passée. Notez que je navigue d'abord vers le dossier souhaité pour pouvoir utiliser les complétions d'onglets (vous ne pouvez pas entre guillemets).

start powershell "-noexit cd..\..\dir\; script.ps1"

Utilisez des guillemets doubles ""pour pouvoir passer des répertoires avec des espaces dans les noms, par exemple,

start powershell "-noexit cd '..\..\my dir'; script.ps1"

IGRACH
la source
0

J'ai fait un one-liner avec la solution de @ JohnL:

$MyInvocation.MyCommand.Path | Split-Path | Push-Location
sashoalm
la source