Как найти каталог текущего скрипта PowerShell на Windows
Узнайте надёжный способ получить путь к текущему скрипту PowerShell. Используем $PSScriptRoot, $PSCommandPath и MyInvocation для совместимости с PS2+.
What’s the best way to figure out where the current PowerShell script is located?
You know, whenever I need to reference a common module or script, I prefer using paths relative to the current script file. That way, my script can always find other scripts in the library without any issues.
So, what’s the most reliable, standard method for determining the directory of the current script? Right now, I’m using this approach:
$MyDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)
I’m aware that in modules (.psm1 files), you can use $PSScriptRoot to get this info, but unfortunately that variable doesn’t get set in regular scripts (like .ps1 files).
What’s the canonical or recommended way to get the current PowerShell script file’s location?
Самый простой и надёжный способ найти каталог выполняемого скрипта – дать PowerShell сам вернуть путь.
- PowerShell 3 + автоматически предоставляет
$PSScriptRoot, который содержит полный путь к каталогу, где находится исполняемый скрипт (или модуль). - Для более старых версий (PowerShell 2) или если хочется быть более точным, используйте
$MyInvocation.MyCommand.Path(или свойствоDefinition) и разбейте его на каталог с помощьюSplit‑Path.
Ниже вы найдёте быстрый справочник и несколько практических советов.
Содержание
- Как работают автоматические переменные
$PSScriptRootи$PSCommandPath - Проверка совместимости и откат к
$MyInvocation - Пример полного скрипта с относительными путями
- Часто задаваемые вопросы
Как работают автоматические переменные $PSScriptRoot и $PSCommandPath
| Переменная | Что хранит | Когда доступна |
|---|---|---|
$PSScriptRoot |
Полный путь к каталогу, содержащему исполняемый скрипт или модуль | PowerShell 3.0+ (внутри скрипта, модуля, или функции, объявленной в файле) |
$PSCommandPath |
Полный путь к файлу скрипта, который сейчас исполняется | PowerShell 5.1+ (внутри скрипта) |
Why
$PSScriptRoot?
It mirrors the behaviour of$PSScriptRootin modules and is the most concise, reliable way to reference files that live alongside your script.
See the official documentation on the PowerShell FAQ for details: PowerShell FAQ – Script root.
Проверка совместимости и откат к $MyInvocation
Если ваш скрипт должен работать в PowerShell 2, используйте MyInvocation:
# В PowerShell 2 и выше
$ScriptPath = $MyInvocation.MyCommand.Path # или .Definition
$ScriptDir = Split-Path -Parent $ScriptPath
Совет – проверяйте наличие
$PSScriptRootперед использованием:
if ($null -ne $PSScriptRoot) {
$ScriptDir = $PSScriptRoot
} else {
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
}
Это гарантирует совместимость и позволяет использовать
$PSScriptRootтам, где он доступен, аMyInvocationв качестве резервного решения.
См. обсуждение на Stack Overflow: What’s the best way to determine the location of the current PowerShell script?.
Пример полного скрипта с относительными путями
# 1️⃣ Определяем каталог текущего скрипта
$ScriptDir = if ($null -ne $PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
# 2️⃣ Путь к модулю, который лежит рядом
$ModulePath = Join-Path -Path $ScriptDir -ChildPath 'MyCommonModule.psm1'
# 3️⃣ Загружаем модуль
Import-Module -Name $ModulePath -Force
# 4️⃣ Теперь можно использовать функции из модуля
Invoke-MyCommonFunction
Что здесь важно?
Join-Pathгарантирует корректную конкатенацию путей на всех ОС.Import-Moduleс-Forceполезен, если скрипт переисполняется.
Часто задаваемые вопросы
| Вопрос | Ответ |
|---|---|
Может ли $PSScriptRoot использоваться в интерактивной сессии? |
Нет – переменная определяется только при выполнении кода из файла. В интерактивной сессии она будет $null. |
| Как получить полное имя файла скрипта, а не только каталог? | Используйте $PSCommandPath (PowerShell 5.1+) или $MyInvocation.MyCommand.Path. |
Что делать, если скрипт запускается через & {} или Invoke-Expression? |
В таких случаях $PSScriptRoot остаётся $null; используйте $MyInvocation.MyCommand.Path в сочетании с Resolve-Path. |
Можно ли использовать $MyInvocation в модулях? |
Да, но $PSScriptRoot предпочтительнее, потому что он всегда указывает на каталог модуля, даже если вызывается из другого скрипта. |
Заключение
- Для PowerShell 3+: просто
"$PSScriptRoot"– самый надёжный способ. - Для совместимости с PowerShell 2:
Split-Path -Parent $MyInvocation.MyCommand.Path. - Если нужен полный путь к файлу:
$PSCommandPath(если доступен) или$MyInvocation.MyCommand.Path. - Всегда проверяйте
$null– в интерактивных сессиях переменные не инициализируются.
С этими правилами ваши скрипты будут находить свои модули и вспомогательные файлы независимо от того, где и как они запускаются.