PowerShell: Führen Sie den Befehl aus dem Skriptverzeichnis aus

144

Ich habe ein PowerShell-Skript, das einige Aufgaben im aktuellen Verzeichnis des Skripts erledigt. Wenn Sie sich also in diesem Verzeichnis befinden, .\script.ps1funktioniert das Ausführen ordnungsgemäß.

Jetzt möchte ich dieses Skript aus einem anderen Verzeichnis aufrufen, ohne das Referenzverzeichnis des Skripts zu ändern. Ich möchte also anrufen ..\..\dir\script.ps1und möchte, dass sich das Skript so verhält, wie es aus seinem Verzeichnis heraus aufgerufen wurde.

Wie mache ich das oder wie ändere ich ein Skript, damit es von jedem Verzeichnis aus ausgeführt werden kann?

Sack
quelle

Antworten:

200

Meinen Sie damit, dass Sie den eigenen Pfad des Skripts möchten, damit Sie auf eine Datei neben dem Skript verweisen können? Versuche dies:

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

Sie können viele Informationen von $ MyInvocation und seinen Eigenschaften erhalten.

Wenn Sie auf eine Datei im aktuellen Arbeitsverzeichnis verweisen möchten, können Sie Resolve-Path oder Get-ChildItem verwenden:

$filepath = Resolve-Path "somefile.txt"

EDIT (basierend auf Kommentar von OP):

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

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Es gibt wahrscheinlich auch andere Möglichkeiten, mit Invoke-Command etwas Ähnliches zu erreichen.

JohnL
quelle
1
Hmm, okay, vielleicht hätte ich etwas klarer über mein Skript sein sollen. Eigentlich ist es ein Skript, das antmit einigen Parametern aufruft . Ich muss also antaus diesem Ordner aufrufen , um sicherzustellen, dass die Konfigurationsdatei korrekt gefunden wird. Idealerweise suche ich nach etwas, um das Ausführungsverzeichnis lokal in diesem Skript vorübergehend zu ändern.
stupsen
3
Ah dann in diesem Fall werden Sie wollen Push-LocationundPop-Location
JohnL
5
Vorsicht, $ MyInvocation ist kontextsensitiv. Normalerweise mache ich $ ScriptPath = (Get-Variable MyInvocation -Scope-Skript) .Value.MyCommand.Path, das von jeder Art von Verschachtelung oder Funktion aus funktioniert
Ion Todirel
39

Wenn Sie native Apps aufrufen, müssen Sie sich keine Gedanken über [Environment]::CurrentDirectorydas $PWDaktuelle Verzeichnis von PowerShell machen . Aus verschiedenen Gründen legt PowerShell beim Festlegen des Speicherorts oder des Push-Speicherorts nicht das aktuelle Arbeitsverzeichnis des Prozesses fest. Sie müssen dies daher sicherstellen, wenn Sie Anwendungen (oder Cmdlets) ausführen, die das Festlegen erwarten.

In einem Skript können Sie Folgendes tun:

$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

Es gibt keine narrensichere Alternative dazu. Viele von uns haben eine Zeile in ihre Eingabeaufforderungsfunktion eingefügt, um [Environment] :: CurrentDirectory festzulegen ... aber das hilft Ihnen nicht, wenn Sie den Speicherort innerhalb eines Skripts ändern .

Zwei Hinweise zum Grund, warum dies von PowerShell nicht automatisch festgelegt wird:

  1. PowerShell kann Multithreading sein. Sie können mehrere Runspaces (siehe RunspacePool und das PSThreadJob-Modul) gleichzeitig in einem einzigen Prozess ausführen. Jeder Runspace hat ein eigenes $PWDArbeitsverzeichnis, aber es gibt nur einen Prozess und nur eine Umgebung.
  2. Selbst wenn Sie Single-Threaded sind, $PWDist dies nicht immer ein legales CurrentDirectory (Sie können beispielsweise eine CD beim Registrierungsanbieter erstellen).

Wenn Sie es in Ihre Eingabeaufforderung einfügen möchten (die nur im Hauptlaufbereich mit einem Thread ausgeführt wird), müssen Sie Folgendes verwenden:

[Environment]::CurrentDirectory = Get-Location -PSProvider FileSystem
Jaykul
quelle
4
Das Arbeitsverzeichnis des Prozesses wird aufgrund von Pfadanbietern nicht geändert: Möglicherweise werden Sie in die Registrierung, eine SQL-Datenbank oder eine IIS-
Struktur
1
Ja. Ich weiß, das war der Punkt dieser letzten "Schlussnotiz". Sie könnten es (wie in meiner Eingabeaufforderungsfunktion) wie jede andere Shell auf der Welt auf den aktuellen Speicherort des Dateisystemanbieters aktualisiert haben.
Jaykul
2
Heilige Skriptgötter, ich bin so enttäuscht .\_/.- für diese getötete Hälfte meines Tages! Leute, im Ernst? Ernsthaft? ..
ulidtko
2
Es ist jetzt größtenteils unnötig, modernere Versionen von PowerShell legen das Arbeitsverzeichnis fest, wenn sie binäre Apps starten.
Jaykul
1
Diese Methode schlägt beim Wiederherstellen des Originals fehl, [Environment]::CurrentDirectorywenn dies nicht der Fall ist $PWD. Das Richtige wäre, es in einer Variablen zu speichern $origEnvDir = [Environment]::CurrentDirectoryund später wiederherzustellen [Environment]::CurrentDirectory = $origEnvDir.
Strom
37

Es gibt Antworten mit einer großen Anzahl von Stimmen, aber als ich Ihre Frage las, dachte ich, Sie wollten das Verzeichnis wissen, in dem sich das Skript befindet, nicht das, in dem das Skript ausgeführt wird. Sie können die Informationen mit den automatischen Variablen von Powershell abrufen

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

Zum Beispiel habe ich ein $ profile-Skript, das die Visual Studio-Lösungsdatei findet und startet. Ich wollte den vollständigen Pfad speichern, sobald eine Lösungsdatei gestartet wurde. Aber ich wollte die Datei speichern, in der das ursprüngliche Skript existiert. Also habe ich $ PsScriptRoot verwendet.

Andy
quelle
12

Ich habe oft den folgenden Code verwendet, um ein Modul zu importieren, das sich im selben Verzeichnis befindet wie das laufende Skript. Es wird zuerst das Verzeichnis abgerufen, aus dem Powershell ausgeführt wird

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

Importmodul "$ currentPath \ sqlps.ps1"

Daniel Wu
quelle
Sie möchten den Bereich auf Skript setzen
Ion Todirel
9

Das würde gut funktionieren.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir
ArunSK
quelle
Vergessen Sie nicht, Pop-Locationam Ende anzurufen , um den Pfad in seinen ursprünglichen Zustand zurückzusetzen.
Lam Le
2

Nun, ich habe eine Weile nach einer Lösung dafür gesucht, ohne irgendwelche Skripte nur von CLI. So mache ich es xD:

  • Navigieren Sie zu dem Ordner, in dem Sie das Skript ausführen möchten (wichtig ist, dass Sie Registerkarten vervollständigen).

    ..\..\dir

  • Umgeben Sie nun die Position mit doppelten Anführungszeichen und fügen Sie sie hinzu cd, damit wir eine weitere Instanz von Powershell aufrufen können.

    "cd ..\..\dir"

  • Fügen Sie einen weiteren Befehl hinzu, um das Skript getrennt durch ;auszuführen. Dabei handelt es sich um ein Befehlstrennzeichen in Powershell

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

  • Führen Sie es schließlich mit einer anderen Instanz von Powershell aus

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

Dadurch wird ein neues Powershell-Fenster geöffnet, das Fenster aufgerufen ..\..\dir, ausgeführt script.ps1und geschlossen.


Beachten Sie, dass ";" Trennt nur Befehle, wie Sie sie einzeln eingegeben haben, wenn der erste fehlschlägt, wird der zweite ausgeführt und der nächste danach und der nächste danach ... Wenn Sie das neue Powershell-Fenster geöffnet lassen möchten, fügen Sie -noexit im übergebenen Befehl hinzu. Beachten Sie, dass ich zuerst zum gewünschten Ordner navigiere, damit ich Tab-Vervollständigungen verwenden kann (Sie konnten nicht in doppelte Anführungszeichen setzen).

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

Verwenden Sie doppelte Anführungszeichen, ""damit Sie Verzeichnisse mit Leerzeichen in Namen übergeben können, z.

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

IGRACH
quelle
0

Ich habe aus der Lösung von @ JohnL einen Einzeiler gemacht:

$MyInvocation.MyCommand.Path | Split-Path | Push-Location
Sashoalm
quelle