Jak mogę uzyskać bieżący plik wykonawczy programu PowerShell?

94

Uwaga: PowerShell 1.0
Chciałbym uzyskać nazwę aktualnie wykonywanego pliku programu PowerShell. To znaczy, jeśli rozpocznę sesję w ten sposób:

powershell.exe .\myfile.ps1

Chciałbym uzyskać ciąg „. \ Myfile.ps1” (lub coś w tym rodzaju). EDYCJA : preferowany jest plik „myfile.ps1” .
Jakieś pomysły?

Ron Klein
źródło
Dzięki, aktualne odpowiedzi są prawie takie same, ale potrzebuję tylko nazwy pliku (a nie całej ścieżki), więc akceptowana odpowiedź to @ Keith. Jednak +1 do obu odpowiedzi. Teraz wiem o sprawie $ MyInvocation :-)
Ron Klein
Co powiesz na pobranie skryptu nadrzędnego z dołączonego skryptu?
Florin Sabau,

Odpowiedzi:

72

Próbowałem podsumować tutaj różne odpowiedzi, zaktualizowane dla PowerShell 5:

  • Jeśli używasz tylko programu PowerShell 3 lub nowszego, użyj $PSCommandPath

  • Jeśli chcesz kompatybilności ze starszymi wersjami, włóż podkładkę:

    if ($PSCommandPath -eq $null) { function GetPSCommandPath() { return $MyInvocation.PSCommandPath; } $PSCommandPath = GetPSCommandPath; }

    To dodaje, $PSCommandPathjeśli jeszcze nie istnieje.

    Kod podkładki można wykonać w dowolnym miejscu (na najwyższym poziomie lub wewnątrz funkcji), chociaż $PSCommandPathzmienna podlega normalnym regułom określania zakresu (np. Jeśli umieścisz podkładkę w funkcji, zmienna będzie ograniczona tylko do tej funkcji).

Detale

Istnieją 4 różne metody używane w różnych odpowiedziach, więc napisałem ten skrypt, aby zademonstrować każdą (plus $PSCommandPath):

function PSCommandPath() { return $PSCommandPath; }
function ScriptName() { return $MyInvocation.ScriptName; }
function MyCommandName() { return $MyInvocation.MyCommand.Name; }
function MyCommandDefinition() {
    # Begin of MyCommandDefinition()
    # Note: ouput of this script shows the contents of this function, not the execution result
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()
}
function MyInvocationPSCommandPath() { return $MyInvocation.PSCommandPath; }

Write-Host "";
Write-Host "PSVersion: $($PSVersionTable.PSVersion)";
Write-Host "";
Write-Host "`$PSCommandPath:";
Write-Host " *   Direct: $PSCommandPath";
Write-Host " * Function: $(ScriptName)";
Write-Host "";
Write-Host "`$MyInvocation.ScriptName:";
Write-Host " *   Direct: $($MyInvocation.ScriptName)";
Write-Host " * Function: $(ScriptName)";
Write-Host "";
Write-Host "`$MyInvocation.MyCommand.Name:";
Write-Host " *   Direct: $($MyInvocation.MyCommand.Name)";
Write-Host " * Function: $(MyCommandName)";
Write-Host "";
Write-Host "`$MyInvocation.MyCommand.Definition:";
Write-Host " *   Direct: $($MyInvocation.MyCommand.Definition)";
Write-Host " * Function: $(MyCommandDefinition)";
Write-Host "";
Write-Host "`$MyInvocation.PSCommandPath:";
Write-Host " *   Direct: $($MyInvocation.PSCommandPath)";
Write-Host " * Function: $(MyInvocationPSCommandPath)";
Write-Host "";

Wynik:

PS C:\> .\Test\test.ps1

PSVersion: 5.1.19035.1

$PSCommandPath:
 *   Direct: C:\Test\test.ps1
 * Function: C:\Test\test.ps1

$MyInvocation.ScriptName:
 *   Direct:
 * Function: C:\Test\test.ps1

$MyInvocation.MyCommand.Name:
 *   Direct: test.ps1
 * Function: MyCommandName

$MyInvocation.MyCommand.Definition:
 *   Direct: C:\Test\test.ps1
 * Function:
    # Begin of MyCommandDefinition()
    # Note this is the contents of the MyCommandDefinition() function, not the execution results
    return $MyInvocation.MyCommand.Definition;
    # End of MyCommandDefinition()


$MyInvocation.PSCommandPath:
 *   Direct:
 * Function: C:\Test\test.ps1

Uwagi:

  • Wykonywany z C:\, ale rzeczywisty skrypt jest C:\Test\test.ps1.
  • Żadna metoda nie informuje o przekazanej ścieżce wywołania ( .\Test\test.ps1)
  • $PSCommandPath jest jedynym niezawodnym sposobem, ale został wprowadzony w programie PowerShell 3
  • W przypadku wersji wcześniejszych niż 3 żadna pojedyncza metoda nie działa zarówno wewnątrz, jak i na zewnątrz funkcji
gregmac
źródło
7
Każdy, kto czyta dzisiaj (2017), powinien przeczytać TEN post jako poprawną odpowiedź! +1
Collin Chaffin,
2
@CollinChaffin: uzgodniono, a obecnie (2017) najmniej obecnie obsługiwany jest system Windows 7, więc nie ma powodu, aby nie używać, $PSCommandPathjeśli starsza wersja (WindowsXP) nie jest wymagana.
tukan
Pierwszy przykład kodu jest wadliwy, ponieważ zawiera dwie definicje tej samej funkcji (function PSCommandPath ) i odniesienie do niewłaściwej funkcji ( Write-Host " * Direct: $PSCommandPath"; Write-Host " * Function: $(ScriptName)";- czy może przeoczę coś oczywistego?
Mike L'Angelo
@ MikeL'Angelo Masz rację! Pozostał niezauważony przez 3 lata. Naprawiono, dziękuję. Wynik i wniosek są jednak takie same.
gregmac
81

Chociaż obecna odpowiedź jest w większości przypadków poprawna, są pewne sytuacje, w których nie da ona poprawnej odpowiedzi. Jeśli używasz wewnątrz swoich funkcji skryptowych, to:

$MyInvocation.MyCommand.Name 

Zwraca nazwę funkcji zamiast nazwy skryptu.

function test {
    $MyInvocation.MyCommand.Name
}

Da ci " test " bez względu na nazwę twojego skryptu. Właściwe polecenie do uzyskania nazwy skryptu to zawsze

$MyInvocation.ScriptName

zwraca pełną ścieżkę do wykonywanego skryptu. Jeśli potrzebujesz tylko nazwy pliku skryptu, ten kod powinien ci pomóc:

split-path $MyInvocation.PSCommandPath -Leaf
Lukas Kucera
źródło
6
Zauważ, że na najwyższym poziomie Scriptname jest niezdefiniowane w eleganckiej wersji 4. Lubię używać na najwyższym poziomie $ MyInvocation.MyCommand.Definition dla pełnej ścieżki lub nazwy, tak jak w przypadku innych odpowiedzi.
AnneTheAgile,
30
$MyInvocation.ScriptNamezwróć pusty ciąg dla mnie, PS v3.0.
JohnC
4
@JohnC $MyInvocation.ScriptNamedziała tylko z wnętrza funkcji. Zobacz moją odpowiedź poniżej .
gregmac
72

Jeśli chcesz tylko nazwę pliku (nie pełną ścieżkę), użyj tego:

$ScriptName = $MyInvocation.MyCommand.Name
Keith Hill
źródło
32

Spróbuj wykonać następujące czynności

$path =  $MyInvocation.MyCommand.Definition 

Może to nie dać ci rzeczywistej wpisanej ścieżki, ale da ci prawidłową ścieżkę do pliku.

JaredPar
źródło
1
@ Hamish pytanie konkretnie mówi, czy zostało wywołane z pliku.
JaredPar
Do Twojej wiadomości: To daje pełną ścieżkę i nazwę pliku (Powershell 2.0)
Ralph Willgoss
Szukałem dokładnie tego polecenia. Dziękuję, JaredPar! :)
sqlfool
Użyj Split-Path, aby uzyskać katalog? $path = Split-Path $MyInvocation.MyCommand.Definition -Parent
Underverse
7

Jeśli szukasz bieżącego katalogu, w którym wykonywany jest skrypt, możesz wypróbować ten:

$fullPathIncFileName = $MyInvocation.MyCommand.Definition
$currentScriptName = $MyInvocation.MyCommand.Name
$currentExecutingPath = $fullPathIncFileName.Replace($currentScriptName, "")

Write-Host $currentExecutingPath
Ryk
źródło
1
To by nie działało poprawnie C:\ilike.ps123\ke.ps1, prawda?
fridojet
@fridojet - Nie jestem pewien, nie w pobliżu terminala PS, aby go przetestować. Dlaczego tego nie spróbujesz i nie zobaczysz?
Ryk
Nie, tylko pytanie retoryczne ;-) - To byłoby po prostu logiczne, ponieważ Replace()metoda zastępuje każde wystąpienie igły (nie tylko ostatnie wystąpienie) i też ją przetestowałem. Jednak dobrym pomysłem jest wykonanie odejmowania na łańcuchach.
fridojet
... A co z String.TrimEnd()( $currentExecutingPath = $fullPathIncFileName.TrimEnd($currentScriptName))? - Działa poprawnie: "Ich bin Hamster".TrimEnd("ster")zwraca Ich bin Hami "Ich bin Hamsterchen".TrimEnd("ster")zwraca Ich bin Hamsterchen(zamiast Ich bin Hamchen) - Dobra!
fridojet
$currentScriptPath = $MyInvocation.MyCommand.Definition; $currentScriptName = $MyInvocation.MyCommand.Name; $currentScriptDir = $currentScriptPath.Substring(0,$currentScriptPath.IndexOf($currentScriptName));
YP
7

uwaga: W przeciwieństwie do $PSScriptRooti $PSCommandPathautomatycznych zmiennych, PSScriptRoota PSCommandPathwłaściwości $MyInvocationzmiennej automatycznej zawierają informacje o wywołującego lub wywołanie skryptu, a nie bieżącego skryptu.

na przykład

PS C:\Users\S_ms\OneDrive\Documents> C:\Users\SP_ms\OneDrive\Documents\DPM ...
=!C:\Users\S_ms\OneDrive\Documents\DPM.ps1

... gdzie DPM.ps1zawiera

Write-Host ("="+($MyInvocation.PSCommandPath)+"!"+$PSCommandPath)
MWR
źródło
4

Twierdziłbym, że istnieje lepsza metoda, ustawiając zakres zmiennej $ MyInvocation.MyCommand.Path:

ex> $ script : MyInvocation.MyCommand.Name

Ta metoda działa we wszystkich okolicznościach wywołania:

Np .: Somescript.ps1

function printme () {
    "In function:"
    ( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
    ( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
    ( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
}
"Main:"
( "MyInvocation.ScriptName: " + [string]($MyInvocation.ScriptName) )
( "script:MyInvocation.MyCommand.Name: " + [string]($script:MyInvocation.MyCommand.Name) )
( "MyInvocation.MyCommand.Name: " + [string]($MyInvocation.MyCommand.Name) )
" "
printme
exit

WYNIK:

PS> powershell C:\temp\test.ps1
Main:
MyInvocation.ScriptName:
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: test.ps1

In function:
MyInvocation.ScriptName: C:\temp\test.ps1
script:MyInvocation.MyCommand.Name: test.ps1
MyInvocation.MyCommand.Name: printme

Zauważ, że powyższa zaakceptowana odpowiedź NIE zwraca wartości, gdy jest wywoływana z Main. Należy również zauważyć, że powyższa zaakceptowana odpowiedź zwraca pełną ścieżkę, gdy pytanie dotyczyło tylko nazwy skryptu. Zmienna o określonym zakresie działa we wszystkich miejscach.

Ponadto, jeśli chcesz mieć pełną ścieżkę, po prostu zadzwoń:

$script:MyInvocation.MyCommand.Path
Daddio
źródło
3

Jak zauważono w poprzednich odpowiedziach, użycie „$ MyInvocation” wiąże się z problemami dotyczącymi zakresu i niekoniecznie zapewnia spójne dane (wartość zwracana w porównaniu z wartością dostępu bezpośredniego). Zauważyłem, że "najczystszą" (najbardziej spójną) metodą uzyskiwania informacji o skrypcie, takich jak ścieżka skryptu, nazwa, parametry, wiersz poleceń itp., Niezależnie od zakresu (w głównych lub kolejnych / zagnieżdżonych wywołaniach funkcji) jest użycie "Get- Zmienna „na„ MyInvocation ”...

# Get the MyInvocation variable at script level
# Can be done anywhere within a script
$ScriptInvocation = (Get-Variable MyInvocation -Scope Script).Value

# Get the full path to the script
$ScriptPath = $ScriptInvocation.MyCommand.Path

# Get the directory of the script
$ScriptDirectory = Split-Path $ScriptPath

# Get the script name
# Yes, could get via Split-Path, but this is "simpler" since this is the default return value
$ScriptName = $ScriptInvocation.MyCommand.Name

# Get the invocation path (relative to $PWD)
# @GregMac, this addresses your second point
$InvocationPath = ScriptInvocation.InvocationName

Możesz więc uzyskać te same informacje, co $ PSCommandPath, ale o wiele więcej w transakcji. Nie jestem pewien, ale wygląda na to, że „Get-Variable” nie było dostępne aż do PS3, więc nie ma zbyt wiele pomocy w przypadku naprawdę starych (niezaktualizowanych) systemów.

Istnieje również kilka interesujących aspektów korzystania z "-Scope", ponieważ można cofnąć się w celu uzyskania nazw itp. Funkcji wywołujących. 0 = prąd, 1 = rodzic itd.

Mam nadzieję, że jest to trochę pomocne.

Ref, https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-variable

AntGut
źródło
1

Przeprowadziłem kilka testów z następującym skryptem, zarówno na PS 2, jak i PS 4 i uzyskałem ten sam wynik. Mam nadzieję, że to pomaga ludziom.

$PSVersionTable.PSVersion
function PSscript {
  $PSscript = Get-Item $MyInvocation.ScriptName
  Return $PSscript
}
""
$PSscript = PSscript
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

""
$PSscript = Get-Item $MyInvocation.InvocationName
$PSscript.FullName
$PSscript.Name
$PSscript.BaseName
$PSscript.Extension
$PSscript.DirectoryName

Wyniki -

Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      -1     -1      

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts

C:\PSscripts\Untitled1.ps1
Untitled1.ps1
Untitled1
.ps1
C:\PSscripts
Mark Crashley
źródło
1

Może to działać w przypadku większości wersji programu PowerShell:

(& { $MyInvocation.ScriptName; })

Może to działać w przypadku zaplanowanego zadania

Get-ScheduledJob |? Name -Match 'JOBNAMETAG' |% Command
mało prawdopodobne
źródło