PowerShell: Uruchom polecenie z katalogu skryptu

144

Mam skrypt PowerShell, który wykonuje pewne czynności przy użyciu bieżącego katalogu skryptu. Więc w tym katalogu, bieganie .\script.ps1działa poprawnie.

Teraz chcę wywołać ten skrypt z innego katalogu bez zmiany katalogu, do którego się odwołuje skrypt. Więc chcę wywołać ..\..\dir\script.ps1i nadal chcę, aby skrypt zachowywał się tak, jak został wywołany z wnętrza swojego katalogu.

Jak to zrobić lub jak zmodyfikować skrypt, aby można go było uruchomić z dowolnego katalogu?

szturchać
źródło

Odpowiedzi:

200

Czy chcesz powiedzieć, że chcesz mieć własną ścieżkę do skryptu, aby móc odwołać się do pliku obok skryptu? Spróbuj tego:

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

Możesz uzyskać wiele informacji z $ MyInvocation i jego właściwości.

Jeśli chcesz odwołać się do pliku w bieżącym katalogu roboczym, możesz użyć Resolve-Path lub Get-ChildItem:

$filepath = Resolve-Path "somefile.txt"

EDYCJA (na podstawie komentarza OP):

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

# do stuff, call ant, etc

# now back to previous directory
Pop-Location

Prawdopodobnie istnieją również inne sposoby osiągnięcia czegoś podobnego za pomocą Invoke-Command.

John L.
źródło
1
Hmm, ok, może powinienem był bardziej precyzyjnie określić mój scenariusz. Właściwie jest to skrypt, który wywołuje antz pewnymi parametrami. Muszę więc zadzwonić antz tego folderu, aby upewnić się, że poprawnie odnajdzie plik konfiguracyjny. Idealnie szukam czegoś do tymczasowej zmiany katalogu wykonywania lokalnie w tym skrypcie.
poke
3
Ach, w takim razie będziesz chciał Push-LocationiPop-Location
JohnL
5
uważaj na to, $ MyInvocation jest zależne od kontekstu, zwykle robię $ ScriptPath = (Get-Variable MyInvocation -Scope Script) .Value.MyCommand.Path, który działa z dowolnego rodzaju zagnieżdżenia lub funkcji
39

Jeśli dzwonisz do aplikacji natywnych, musisz się martwić, że [Environment]::CurrentDirectorynie chodzi o $PWDbieżący katalog programu PowerShell . Z różnych powodów program PowerShell nie ustawia bieżącego katalogu roboczego procesu podczas ustawiania lokalizacji lub lokalizacji wypychania, więc musisz się upewnić, że robisz to, jeśli używasz aplikacji (lub poleceń cmdlet), które oczekują, że zostanie ustawiony.

W skrypcie możesz to zrobić:

$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

Nie ma niezawodnej alternatywy. Wielu z nas umieściło linię w naszej funkcji zachęty, aby ustawić [Środowisko] :: CurrentDirectory ... ale to nie pomaga, gdy zmieniasz lokalizację w skrypcie.

Dwie uwagi dotyczące powodu, dla którego nie jest to ustawiane automatycznie przez program PowerShell:

  1. PowerShell może być wielowątkowy. Możesz mieć wiele obszarów Runspace (patrz RunspacePool i moduł PSThreadJob) działających jednocześnie w ramach jednego procesu. Każdy obszar roboczy ma swój własny $PWDkatalog roboczy, ale jest tylko jeden proces i tylko jedno środowisko.
  2. Nawet jeśli jesteś jednowątkowy, $PWDnie zawsze jest to legalny katalog CurrentDirectory (możesz na przykład CD do dostawcy rejestru).

Jeśli chcesz umieścić go w swoim znaku zachęty (który działałby tylko w głównym obszarze działania, jednowątkowy), musisz użyć:

[Environment]::CurrentDirectory = Get-Location -PSProvider FileSystem
Jaykul
źródło
4
Nie zmienia to katalogu roboczego procesu ze względu na dostawców ścieżek: możesz wchodzić na CD do rejestru, bazy danych SQL lub gałęzi IIS itp ...
piers7
1
Tak. Wiem, o to właśnie chodziło w ostatniej „Nucie końcowej”. Mogli (tak jak ja w mojej funkcji zachęty) zaktualizować go do bieżącej lokalizacji dostawcy FileSystem, tak jak każda inna powłoka na świecie.
Jaykul
2
Święci bogowie, skrypty, jestem TAK ROZCZAROWANY .\_/.- bo to zabiło połowę mojego dnia! Ludzie, poważnie? Poważnie? ..
ulidtko
2
Jest to teraz przeważnie niepotrzebne, bardziej nowoczesne wersje PowerShell ustawiają katalog roboczy podczas uruchamiania aplikacji binarnych.
Jaykul
1
Ta metoda nie przywraca oryginału, [Environment]::CurrentDirectoryjeśli różni się on od $PWD. Prawidłową rzeczą byłoby zapisanie go w zmiennej, $origEnvDir = [Environment]::CurrentDirectorya następnie przywrócenie go później [Environment]::CurrentDirectory = $origEnvDir.
Strom
37

Są odpowiedzi z dużą liczbą głosów, ale kiedy przeczytałem twoje pytanie, pomyślałem, że chcesz poznać katalog, w którym znajduje się skrypt, a nie ten, w którym skrypt jest uruchomiony. Możesz uzyskać informacje za pomocą zmiennych automatycznych PowerShell

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

Na przykład mam skrypt $ profile, który znajduje plik rozwiązania Visual Studio i uruchamia go. Chciałem zapisać pełną ścieżkę po uruchomieniu pliku rozwiązania. Ale chciałem zapisać plik, w którym istnieje oryginalny skrypt. Więc użyłem $ PsScriptRoot.

Andy
źródło
12

Często używałem poniższego kodu do importowania modułu, który znajduje się w tym samym katalogu, co uruchomiony skrypt. Najpierw pobierze katalog, z którego działa PowerShell

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

moduł importu „$ currentPath \ sqlps.ps1”

Daniel Wu
źródło
chcesz ustawić
9

To by działało dobrze.

Push-Location $PSScriptRoot

Write-Host CurrentDirectory $CurDir
ArunSK
źródło
Nie zapomnij wywołać Pop-Locationna końcu, aby przywrócić ścieżkę do jej pierwotnego stanu.
Lam Le
2

Cóż, szukałem przez jakiś czas rozwiązania tego problemu, bez żadnych skryptów tylko z CLI. Tak to robię xD:

  • Przejdź do folderu, z którego chcesz uruchomić skrypt (ważne jest to, że masz wypełnienia kart)

    ..\..\dir

  • Teraz otocz lokalizację podwójnymi cudzysłowami, a wewnątrz nich dodaj cd, abyśmy mogli wywołać kolejną instancję programu PowerShell.

    "cd ..\..\dir"

  • Dodaj kolejne polecenie, aby uruchomić skrypt oddzielony ;znakiem, za pomocą jest separatorem poleceń w programie PowerShell

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

  • Wreszcie uruchom go z inną instancją programu PowerShell

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

Spowoduje to otwarcie nowego okna programu PowerShell, przejście do ..\..\dir, uruchomienie script.ps1i zamknięcie okna.


Zwróć uwagę, że ";" po prostu oddziela polecenia, tak jak wpisałeś je jedno po drugim, jeśli pierwsza zawiedzie, druga uruchomi się i następna po, a następnie po ... Jeśli chcesz pozostawić otwarte nowe okno PowerShell, dodaj -noexit w przekazanym poleceniu. Zauważ, że najpierw przechodzę do żądanego folderu, aby móc użyć uzupełniania tabulatorów (nie możesz w cudzysłowach).

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

Użyj podwójnych cudzysłowów, ""aby móc przekazywać katalogi ze spacjami w nazwach, np.

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

IGRACH
źródło
0

Zrobiłem jedną linijkę z rozwiązania @ JohnL:

$MyInvocation.MyCommand.Path | Split-Path | Push-Location
sashoalm
źródło