Как найти исходный путь для выполнения script?

Я хочу, чтобы узнать, от какого пути выполнялся мой запуск script.
Это часто не будет $pwd.

Мне нужно вызвать другие скрипты, которые находятся в структуре папок относительно моего script, и, хотя я мог бы жестко кодировать пути, которые и неприятны, и немного боль в шее при попытке продвинуть с "dev" "проверить" на "производство".

Ответ 1

Вездесущий script изначально размещенный Джеффри Сновер команды PowerShell (данный в Skyler answer), а вариации, опубликованные Keith Cedirc и EBGreen, страдают от серьезного недостатка - независимо от того, сообщает ли код, что вы ожидаете, от того, где вы его называете!

Мой код ниже преодолевает эту проблему, просто ссылаясь на область script вместо parent:

function Get-ScriptDirectory
{
    Split-Path $script:MyInvocation.MyCommand.Path
}

Чтобы проиллюстрировать проблему, я создал тестовое средство, которое анализирует целевое выражение четырьмя различными способами. (В скобках указаны ключи к следующей таблице результатов.)

  • встроенный код [inline]
  • встроенная функция, т.е. функция в основной программе [встроенная функция]
  • Функция Dot-sourced, т.е. одна и та же функция переместилась в отдельный файл .ps1 [источник точки]
  • Функция модуля, т.е. одна и та же функция переместилась в отдельный файл .psm1 [module]

Последние два столбца показывают результат использования области script (т.е. $script:) или с родительской областью (с -scope 1). Результат "script" означает, что обращение правильно сообщило о местоположении script. Результат "модуля" означает, что вызов сообщил местоположение модуля, содержащего функцию, а не script, который вызвал функцию; это указывает на недостаток обеих функций, которые вы не можете поместить в модуль.

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

table of input combinations

Наконец, вот тестовое транспортное средство:

function DoubleNested()
{
    "=== DOUBLE NESTED ==="
    NestCall
}

function NestCall()
{
    "=== NESTED ==="
    "top level:"
    Split-Path $script:MyInvocation.MyCommand.Path
    #$foo = (Get-Variable MyInvocation -Scope 1).Value
    #Split-Path $foo.MyCommand.Path
    "immediate func call"
    Get-ScriptDirectory1
    "dot-source call"
    Get-ScriptDirectory2
    "module call"
    Get-ScriptDirectory3
}

function Get-ScriptDirectory1
{
    Split-Path $script:MyInvocation.MyCommand.Path
    # $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    # Split-Path $Invocation.MyCommand.Path
}

. .\ScriptDirFinder.ps1
Import-Module ScriptDirFinder -force

"top level:"
Split-Path $script:MyInvocation.MyCommand.Path
#$foo = (Get-Variable MyInvocation -Scope 1).Value
#Split-Path $foo.MyCommand.Path

"immediate func call"
Get-ScriptDirectory1
"dot-source call"
Get-ScriptDirectory2
"module call"
Get-ScriptDirectory3

NestCall
DoubleNested

Содержание ScriptDirFinder.ps1:

function Get-ScriptDirectory2
{
    Split-Path $script:MyInvocation.MyCommand.Path
#   $Invocation = (Get-Variable MyInvocation -Scope 1).Value
#   Split-Path $Invocation.MyCommand.Path
}

Содержимое ScriptDirFinder.psm1:

function Get-ScriptDirectory3
{
    Split-Path $script:MyInvocation.MyCommand.Path
#   $Invocation = (Get-Variable MyInvocation -Scope 1).Value
#   Split-Path $Invocation.MyCommand.Path
}

Я не знаком с тем, что было введено в PowerShell 2, но вполне возможно, что область script не существовала в PowerShell 1, в то время, когда Джеффри Сноувер опубликовал свой пример.

Я был удивлен, когда, хотя я нашел, что его пример кода широко распространен в Интернете, он сразу же удался, когда я попробовал! Но это было потому, что я использовал его по-другому, чем пример Snover (я назвал его не в script -top, а изнутри другой функции (мой пример "вложенной дважды" ).

2011.09.12 Обновление

Вы можете прочитать об этом с другими советами и трюками по модулям в моей только что опубликованной статье на Simple-Talk.com: Дальше вниз по кроличьей дыре: модули и инкапсуляция PowerShell.

Ответ 2

Вы отметили свой вопрос для версии Powershell версии 1.0, однако, если у вас есть доступ к версии Powershell версии 3.0, вы знаете, что есть $PSCommandPath и $PSScriptRoot, что упрощает получение пути script. Дополнительную информацию см. В разделе "OTHER script ОСОБЕННОСТИ" на этой странице.

Ответ 3

Мы используем такой код в большинстве наших скриптов в течение нескольких лет без проблем:

#--------------------------------------------------------------------
# Dot source support scripts
#--------------------------------------------------------------------
$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir  = Split-Path -Parent $ScriptPath
. $ScriptDir\BuildVars.ps1
. $ScriptDir\LibraryBuildUtils.ps1
. $ScriptDir\BuildReportUtils.ps1

Ответ 4

Я думаю, вы можете найти путь к вашему запуску script, используя

$MyInvocation.MyCommand.Path

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

Седрик

Ответ 5

Недавно я столкнулся с той же проблемой. Следующая статья помогла мне решить проблему: http://blogs.msdn.com/powershell/archive/2007/06/19/get-scriptdirectory.aspx

Если вам не интересно, как это работает, вот весь код, который вам нужен в статье:

function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}

И затем вы получите путь, просто сделав:

$path = Get-ScriptDirectory

Ответ 6

Это одна из тех странностей (по крайней мере, на мой взгляд) в PS. Я уверен, что для этого есть совершенно веская причина, но мне все еще кажется странным. Итак:

Если вы находитесь в script, но не в функции, тогда $myInvocation.InvocationName предоставит вам полный путь, включая имя script. Если вы находитесь в script и внутри функции, то $myInvocation.ScriptName даст вам то же самое.

Ответ 7

Спасибо, msorens! Это действительно помогло мне с моим настраиваемым модулем. В случае, если кто-то заинтересован в создании собственного, вот как моя структурирована.

MyModule (folder)
 - MyModule.psd1 (help New-ModuleManifest)
 - MyScriptFile.ps1 (ps1 files are easy to test)

Затем вы указываете MyScriptFile.ps1 в MyModule.psd1. Ссылка на .ps1 в массиве NestedModules поместит функции в состояние сеанса модуля, а не в состояние глобального сеанса. (Как написать манифест модуля)

NestedModules = @('.\MyScriptFile.ps1','.\MyOtherScriptFile.ps1')

Содержимое MyScriptFile.ps1

function Get-ScriptDirectory {
    Split-Path $script:MyInvocation.MyCommand.Path
}

try {
    Export-ModuleMember -Function "*-*"
}
catch{}

Try/catch скрывает ошибку от Export-ModuleMember при запуске MyScriptFile.ps1

Скопируйте каталог MyModule на один из найденных здесь путей: $env: PSModulePath

PS C:\>Import-Module MyModule
PS C:\>Get-Command -Module MyModule

CommandType     Name                                               ModuleName                                                                                                                                                
-----------     ----                                               ----------                                                                                                                                                
Function        Get-ScriptDirectory                                MyModule