Jak uzyskać bieżący katalog wykonywanego polecenia cmdlet

202

To powinno być proste zadanie, ale widziałem kilka prób uzyskania ścieżki do katalogu, w którym znajduje się wykonane polecenie cmdlet, z różnym powodzeniem. Na przykład, kiedy wykonuję C:\temp\myscripts\mycmdlet.ps1plik z ustawieniami C:\temp\myscripts\settings.xml, chciałbym móc przechowywać C:\temp\myscriptsw nim zmienną mycmdlet.ps1.

Jest to jedno rozwiązanie, które działa (choć nieco uciążliwe):

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

Kolejne sugerowało to rozwiązanie, które działa tylko w naszym środowisku testowym:

$settingspath = '.\settings.xml'

Bardzo podoba mi się to drugie podejście i wolę, aby za każdym razem analizować ścieżkę do pliku jako parametr, ale nie mogę zmusić go do działania w moim środowisku programistycznym. Co mam zrobić? Czy ma to coś wspólnego z konfiguracją programu PowerShell?

Stig Perez
źródło
11
Zauważ, że niejednoznaczny tytuł tego pytania zaowocował odpowiedziami poniżej rozwiązującymi jeden z dwóch odrębnych problemów, bez wyraźnego stwierdzenia, które: (a) jak odwoływać się do bieżącej lokalizacji (katalogu) lub (b) jak odwoływać się do lokalizacji uruchomionego skryptu ( katalog, w którym znajduje się działający skrypt, który może, ale nie musi być bieżącym katalogiem).
mklement0

Odpowiedzi:

143

Niezawodny sposób na zrobienie tego jest dokładnie taki, jak pokazałeś $MyInvocation.MyCommand.Path.

Korzystanie ze ścieżek względnych będzie oparte na $ pwd w programie PowerShell, bieżącym katalogu aplikacji lub bieżącym katalogu roboczym interfejsu API .NET.

PowerShell v3 + :

Użyj zmiennej automatycznej $PSScriptRoot.

JasonMArcher
źródło
6
Czy możesz mi wyjaśnić, w jaki sposób znalazłeś ŚCIEŻKĘ? $ MyInvocation.MyCommand | gm nie wyświetla takiej właściwości na liście członków.
Vitaliy Markitanov,
19
dlaczego nie użyć po prostu $ PSScriptRoot? Wydaje się bardziej niezawodny
mBrice1024 27.07.17
@ user2326106 Czy możesz wyjaśnić różnicę między $PSScriptRooti $MyInvocation.MyCommand.Path?
duct_tape_coder
1
Ta odpowiedź jest niepełna.
K - Toksyczność w SO rośnie.
Ta odpowiedź jest niepełna.
stackleit,
263

Tak, to powinno działać. Ale jeśli potrzebujesz zobaczyć ścieżkę bezwzględną, to wszystko, czego potrzebujesz:

(Get-Item -Path ".\").FullName
GuruKay
źródło
4
Dzięki, to świetny sposób na znalezienie pełnej ścieżki ze ścieżek względnych. Np. (Get-Item -Path $ myRelativePath -Verbose) .FullName
dlux
Dziękuję Ci za to. Inne odpowiedzi nie działały dla skryptów Powershell skompilowanych do EXE.
Zach Alexander
10
Jest źle . Pobiera bieżący katalog procesu , który może być w dowolnym miejscu. Na przykład, jeśli bieżącym katalogiem wiersza poleceń jest C:\mydiri wywołam polecenie C:\dir1\dir2\dir3\mycmdlet.ps1, rozwiąże to C:\mydir, a nie C:\dir1\dir2\dir3. Wywołanie nowego pliku wykonywalnego ma ten sam problem, ponieważ bieżący katalog jest dziedziczony z procesu nadrzędnego.
jpmc26
85

Najłatwiejszą metodą wydaje się być użycie następującej predefiniowanej zmiennej:

 $PSScriptRoot

about_Automatic_Variablesi about_Scriptsoba stwierdzają:

W PowerShell 2.0 ta zmienna jest poprawna tylko w modułach skryptowych (.psm1). Począwszy od PowerShell 3.0, obowiązuje we wszystkich skryptach.

Używam tego w ten sposób:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName
Tony Sheen
źródło
12
Jest to specyficzne dla wersji. Wymaga to co najmniej Powershell 3.0.
Marvin Dickhaus,
1
Właśnie tego potrzebowałem, aby odwołać się do pliku w tej samej lokalizacji co skrypt - dzięki!
Adam Prescott
Jest to najlepsza odpowiedź, ponieważ daje ci dokładnie ścieżkę, w której znajduje się twój skrypt PS, który jest zasadniczo rdzeniem do wykonania skryptu. Nie ma znaczenia, jaki jest twój obecny katalog roboczy, z którego wywołałeś skrypty. +1.
RBT
@MarvinDickhaus Dlatego w większości twoich skryptów trzeba używać „Set-StrictMode -Version 3.0” :) Dziękujemy za linki!
Alexander Shapkin
44

Możesz także użyć:

(Resolve-Path .\).Path

Część w nawiasach zwraca PathInfoobiekt.

(Dostępne od wersji PowerShell 2.0.)

Alex Angas
źródło
2
Jest źle . Pobiera bieżący katalog procesu , który może być w dowolnym miejscu. Na przykład, jeśli bieżącym katalogiem wiersza poleceń jest C:\mydiri wywołam polecenie C:\dir1\dir2\dir3\mycmdlet.ps1, rozwiąże to C:\mydir, a nie C:\dir1\dir2\dir3. Wywołanie nowego pliku wykonywalnego ma ten sam problem, ponieważ bieżący katalog jest dziedziczony z procesu nadrzędnego.
jpmc26
3
Dzięki! Również źle zrozumiałem tytuł tego pytania, a odpowiedź była dokładnie tym, czego szukałem. Jednak ... To nie odpowiada na pytanie.
Ryan The Leach
33

Ścieżka jest często zerowa. Ta funkcja jest bezpieczniejsza.

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
źródło
1
dlaczego - zakres 1? not
-Scope
1
Zmienna Get: Numer zakresu „1” przekracza liczbę aktywnych zakresów.
suiwenfeng
2
Ten błąd występuje, ponieważ nie masz zasięgu nadrzędnego. -Scope parametr pobiera zmienną w określonym zakresie. 1 w tym przypadku jest zakresem nadrzędnym. Aby uzyskać więcej informacji, zobacz ten artykuł w technecie na temat Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Christian Flem
27

Próbować :

(Get-Location).path

lub:

($pwd).path
Rohin Sidharth
źródło
Ciągle zapominam o $pwd/ $PWDza każdym razem, gdy robię sobie długą przerwę od PowerShell! O wiele bardziej przydatne IMO ...
kayleeFrye_onDeck
17

Get-Location zwróci bieżącą lokalizację:

$Currentlocation = Get-Location
Veera Induvasi
źródło
3
PS C: \ Windows \ system32> C: \ powershell \ checkfile.ps1 -> to da c: \ windows \ system32
nbi
11

Podoba mi się rozwiązanie jednowierszowe :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
FrankyHollywood
źródło
To jak dotąd najlepsze rozwiązanie.
Teoman shipahi
Tak, działa dla mnie. Mój pwd to mój katalog użytkownika i uruchamiam skrypt w innym katalogu (B), który daje (B)
andrew pate
8

Spróbuj tego:

$WorkingDir = Convert-Path .
Siedmiokrotny Exurbanite
źródło
4

W Powershell 3 i nowszych możesz po prostu użyć

$PSScriptRoot

BlackSpy
źródło
1

Można by pomyśleć, że użycie „. \” Jako ścieżki oznacza, że ​​jest to ścieżka wywołania. Ale nie przez cały czas. Przykład, jeśli używasz go w zadaniu ScriptBlock. W takim przypadku może to wskazywać na% profil% \ Documents.

syanzy
źródło
1

ta funkcja ustawi lokalizację zachęty do ścieżki skryptu, zajmując się różnymi sposobami uzyskania ścieżki skryptu między vscode, psise i 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
źródło
0

Dla tego, co warto być rozwiązaniem jednoliniowym, poniżej jest dla mnie działające rozwiązanie.

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

1 na końcu ma zignorować /.

Dzięki powyższym postom za pomocą polecenia cmdlet Get-Location .

Ak777
źródło
0

Większość odpowiedzi nie działa podczas debugowania następujących IDE:

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

Ponieważ w tych $PSScriptRootjest pusty i Resolve-Path .\(i podobne) spowoduje nieprawidłowe ścieżki.

Odpowiedź Freakydinde'a jest jedyną, która rozwiązuje te sytuacje, więc głosowałem za nią, ale nie sądzę, Set-Locationaby odpowiedź była naprawdę pożądana. Naprawiłem to i uczyniłem kod nieco jaśniejszym:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }
Mariano Desanze
źródło
-1

Aby rozwinąć odpowiedź @Cradle: możesz również napisać funkcję wielofunkcyjną, która da ci ten sam wynik na pytanie PO:

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
źródło
-1

Jeśli potrzebujesz tylko nazwy bieżącego katalogu, możesz zrobić coś takiego:

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

Zakładając, że pracujesz z C: \ Temp \ Location \ MyWorkingDirectory>

Wynik

MyWorkingDirectory

SolThoth
źródło
-1

Miałem podobne problemy i sprawiało mi to dużo problemów, ponieważ tworzę programy napisane w PowerShell (aplikacje GUI dla użytkowników końcowych) i mam dużo plików i zasobów, które muszę załadować z dysku. Z mojego doświadczenia .wynika , że używanie do reprezentowania bieżącego katalogu jest zawodne. Powinien reprezentować bieżący katalog roboczy, ale często nie. Wygląda na to, że PowerShell zapisuje lokalizację, z której wywołano PowerShell .. Mówiąc ściślej, przy pierwszym uruchomieniu PowerShell, domyślnie uruchamia się w katalogu użytkownika domowego. Zazwyczaj jest to katalog twojego konta użytkownika, coś w rodzajuC:\USERS\YOUR USER NAME. Następnie PowerShell zmienia katalog na katalog, z którego został wywołany, lub na katalog, w którym znajduje się wykonywany skrypt, przed wyświetleniem monitu programu PowerShell lub uruchomieniem skryptu. Ale dzieje się tak, gdy sama aplikacja PowerShell uruchamia się pierwotnie w katalogu użytkownika domowego.

I .reprezentuje ten początkowy katalog, w którym uruchomiono PowerShell. .Reprezentuje więc tylko bieżący katalog na wypadek, gdyby PowerShell został wywołany z poszukiwanego katalogu. Jeśli później zmienisz katalog w kodzie programu PowerShell, zmiana nie pojawi się .w każdym przypadku. W niektórych przypadkach .reprezentuje bieżący katalog roboczy, aw innych katalog, z którego wywołano PowerShell (sam, a nie skrypt), co może prowadzić do niespójnych wyników. Z tego powodu używam skryptu wywołującego. Skryptu PowerShell z pojedynczym wewnątrz polecenia: POWERSHELL. To zapewni, że PowerShell zostanie wywołany z poszukiwanego katalogu, a tym samym.reprezentują bieżący katalog. Działa to tylko wtedy, gdy później nie zmienisz katalogu w kodzie programu PowerShell. W przypadku scenariusza, używam skryptu wywołującego, który jest podobny do ostatniego już wspomniałem, oprócz tego, że zawiera opcję pliku: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Zapewnia to uruchomienie programu PowerShell w bieżącym katalogu roboczym.

Po prostu kliknięcie skryptu wywołuje program PowerShell z katalogu domowego użytkownika bez względu na to, gdzie znajduje się skrypt. Wynika to z tego, że bieżącym katalogiem roboczym jest katalog, w którym znajduje się skrypt, ale katalogiem wywołania programu PowerShell jest C:\USERS\YOUR USER NAME, a .zwracanie jednego z tych dwóch katalogów w zależności od sytuacji jest śmieszne.

Jednak aby uniknąć zamieszania i to wszystko za pomocą skryptu wywołującego, można po prostu użyć jednej $PWDlub $PSSCRIPTROOTzamiast .reprezentować bieżącego katalogu w zależności od pogody chcesz reprezentować bieżący katalog lub katalog, z którego scenariusz został wywołany. A jeśli z jakiegoś powodu chcesz odzyskać inny z dwóch .zwracanych katalogów , możesz użyć $HOME.

Osobiście mam po prostu skrypt wywołujący w katalogu głównym moich aplikacji, które rozwijam za pomocą programu PowerShell, który wywołuje mój główny skrypt aplikacji i po prostu pamiętam, aby nigdy nie zmieniać bieżącego katalogu roboczego w kodzie źródłowym mojej aplikacji, więc nigdy nie muszę się o to martwić, i mogę używać .do reprezentowania bieżącego katalogu i do obsługi względnego adresowania plików w moich aplikacjach bez żadnych problemów. Powinno to działać w nowszych wersjach programu PowerShell (nowszych niż wersja 2).

SYOB SYOT
źródło