Как я могу получить текущий исполняемый файл PowerShell?


94

Примечание. PowerShell 1.0
Я хотел бы получить имя текущего исполняемого файла PowerShell. То есть, если я начну сеанс так:

powershell.exe .\myfile.ps1

Я хочу получить строку ". \ Myfile.ps1" (или что-то в этом роде). РЕДАКТИРОВАТЬ : предпочтительнее myfile.ps1 .
Любые идеи?


Спасибо, текущие ответы почти такие же, но мне нужно только имя файла (а не весь путь), поэтому принятый ответ - @ Keith's. +1 к обоим ответам. Теперь я знаю о штуке $ MyInvocation :-)
Рон Кляйн

Как насчет получения родительского сценария из включенного сценария?
Флорин Сабау

Ответы:


73

Я попытался обобщить здесь различные ответы, обновленные для PowerShell 5:

  • Если вы используете только PowerShell 3 или выше, используйте $PSCommandPath

  • Если нужна совместимость со старыми версиями, вставьте прокладку:

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

    Это добавляет, $PSCommandPathесли он еще не существует.

    Код прокладки может выполняться где угодно (на верхнем уровне или внутри функции), хотя $PSCommandPathпеременная подчиняется обычным правилам области видимости (например, если вы помещаете прокладку в функцию, переменная ограничивается только этой функцией).

Детали

В разных ответах используются 4 разных метода, поэтому я написал этот сценарий, чтобы продемонстрировать каждый (плюс $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 "";

Выход:

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

Примечания:

  • Выполняется из C:\, но фактический сценарий есть C:\Test\test.ps1.
  • Ни один метод не сообщает вам переданный путь вызова ( .\Test\test.ps1)
  • $PSCommandPath единственный надежный способ, но он был представлен в PowerShell 3
  • Для версий до 3 ни один метод не работает как внутри функции, так и вне ее.

7
Для всех, кто читает сегодня (2017 г.), они должны прочитать ЭТО сообщение как правильный ответ! +1
Коллин

2
@CollinChaffin: согласен, и теперь (2017 г.) наименее поддерживаемой в настоящее время является Windows 7, поэтому нет причин не использовать ее, $PSCommandPathесли устаревшая версия (WindowsXP) не требуется.
Tukan

Первый пример кода ошибочен, поскольку он содержит два определения одной и той же функции ( function PSCommandPath) и ссылку на неправильную функцию ( Write-Host " * Direct: $PSCommandPath"; Write-Host " * Function: $(ScriptName)";- или я упускаю из виду что-то очевидное?
Майк

@ MikeL'Angelo Вы правы! Осталась незамеченной 3 года. Исправлено, спасибо. Однако вывод и вывод такие же.
gregmac

81

Хотя текущий ответ в большинстве случаев является правильным, в некоторых ситуациях он не даст вам правильный ответ. Если вы используете внутри скрипта функции, то:

$MyInvocation.MyCommand.Name 

Возвращает имя функции вместо имени скрипта.

function test {
    $MyInvocation.MyCommand.Name
}

Даст вам " тест " независимо от того, как называется ваш скрипт. Правильная команда для получения имени скрипта всегда

$MyInvocation.ScriptName

это возвращает полный путь к сценарию, который вы выполняете. Если вам нужно только имя файла сценария, этот код должен вам помочь:

split-path $MyInvocation.PSCommandPath -Leaf

6
Обратите внимание, что на верхнем уровне Scriptname не определено с posh v4. Мне нравится использовать на верхнем уровне $ MyInvocation.MyCommand.Definition для полного пути или имени в соответствии с другими ответами.
AnneTheAgile,

30
$MyInvocation.ScriptNameвернуть пустую строку для меня, PS v3.0.
JohnC

4
@JohnC $MyInvocation.ScriptNameработает только внутри функции. Смотрите мой ответ ниже .
gregmac

72

Если вам нужно только имя файла (а не полный путь), используйте это:

$ScriptName = $MyInvocation.MyCommand.Name

32

Попробуйте следующее

$path =  $MyInvocation.MyCommand.Definition 

Это может не дать вам фактического введенного пути, но даст вам действительный путь к файлу.


1
@Hamish в вопросе конкретно говорится, если он вызывается из файла.
JaredPar

К вашему сведению: это даст вам полный путь и имя файла (Powershell 2.0)
Ральф Уиллгосс

Искал именно эту команду. Спасибо, ДжаредПар! :)
sqlfool

Использовать Split-Path для получения каталога? $path = Split-Path $MyInvocation.MyCommand.Definition -Parent
Underverse

7

Если вы ищете текущий каталог, в котором выполняется сценарий, вы можете попробовать этот:

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

Write-Host $currentExecutingPath

1
Это не сработает правильно C:\ilike.ps123\ke.ps1, не так ли?
fridojet 06

@fridojet - Не уверен, не рядом с терминалом PS, чтобы проверить это. Почему бы тебе не попробовать и не посмотреть?
Ryk

Нет, просто риторический вопрос ;-) - Это было бы просто логично, потому что Replace()метод заменяет все вхождения иглы (а не только последнее вхождение), и я также его тестировал. Однако неплохо было бы сделать что-то вроде вычитания на строках.
fridojet 07

... А как насчет String.TrimEnd()( $currentExecutingPath = $fullPathIncFileName.TrimEnd($currentScriptName))? - Работает правильно: "Ich bin Hamster".TrimEnd("ster")возвращает Ich bin Hamи "Ich bin Hamsterchen".TrimEnd("ster")возвращает Ich bin Hamsterchen(вместо Ich bin Hamchen) - Отлично!
fridojet 07

$currentScriptPath = $MyInvocation.MyCommand.Definition; $currentScriptName = $MyInvocation.MyCommand.Name; $currentScriptDir = $currentScriptPath.Substring(0,$currentScriptPath.IndexOf($currentScriptName));
YP

7

остерегайтесь: В отличии от $PSScriptRootи $PSCommandPathавтоматического переменных, то PSScriptRootи PSCommandPathсвойства $MyInvocationавтоматического переменного содержит информацию о запустивших или вызывающем сценарии, а не текущий сценария.

например

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

... где DPM.ps1содержится

Write-Host ("="+($MyInvocation.PSCommandPath)+"!"+$PSCommandPath)

4

Я бы сказал, что есть лучший метод, установив область видимости переменной $ MyInvocation.MyCommand.Path:

ex> $ script : MyInvocation.MyCommand.Name

Этот метод работает при любых обстоятельствах вызова:

EX: 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

ВЫХОД:

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

Обратите внимание, как принятый выше ответ НЕ возвращает значение при вызове из Main. Также обратите внимание, что принятый выше ответ возвращает полный путь, если в вопросе запрашивается только имя сценария. Переменная с областью видимости работает везде.

Кроме того, если вам нужен полный путь, вы просто позвоните:

$script:MyInvocation.MyCommand.Path

3

Как отмечалось в предыдущих ответах, использование «$ MyInvocation» связано с проблемами области видимости и не обязательно обеспечивает согласованные данные (возвращаемое значение или значение прямого доступа). Я обнаружил, что «самый чистый» (наиболее последовательный) метод получения информации о сценарии, такой как путь к сценарию, имя, параметры, командная строка и т. Д., Независимо от области действия (в основных или последующих / вложенных вызовах функций), заключается в использовании «Get- Переменная "в" 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

Таким образом, вы можете получить ту же информацию, что и $ PSCommandPath, но гораздо больше в сделке. Не уверен, но похоже, что «Get-Variable» не было доступно до PS3, так что не очень помогает для действительно старых (не обновленных) систем.

Есть также некоторые интересные аспекты при использовании «-Scope», поскольку вы можете вернуться назад, чтобы получить имена и т. Д. Вызывающих функций. 0 = текущий, 1 = родительский и т. Д.

Надеюсь, это поможет.

Ссылка, https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-variable


1

Провел некоторое тестирование со следующим скриптом как на PS 2, так и на PS 4 и дало тот же результат. Надеюсь, это поможет людям.

$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

Полученные результаты -

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

1

Это может работать в большинстве версий PowerShell:

(& { $MyInvocation.ScriptName; })

Это может работать для запланированного задания

Get-ScheduledJob |? Name -Match 'JOBNAMETAG' |% Command
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.