Как я могу найти исходный путь к исполняемому скрипту?[дубликат]

StackOverflow https://stackoverflow.com/questions/801967

  •  03-07-2019
  •  | 
  •  

Вопрос

Я хочу иметь возможность определить, по какому пути был запущен мой исполняемый скрипт.
Часто это не будет $ pwd.

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

Это было полезно?

Решение

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

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

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

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

  1. встроенный код [inline]
  2. встроенная функция, т.е.функция в основной программе [встроенная функция]
  3. Функция с точечным источником, т.е.та же функция перенесена в отдельный файл .ps1 [точечный исходный код]
  4. Модульная функция, т.е.та же функция перенесена в отдельный файл .psm1 [модуль]

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

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

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 scope не существовал в PowerShell 1 на момент публикации Джеффри Сновером своего примера.

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

Обновление 2011.09.12

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

Другие советы

Вы отметили свой вопрос для Powershell версии 1.0, однако, если у вас есть доступ к Powershell версии 3.0, вы знаете, что $PSCommandPathи$PSScriptRootэто немного упрощает получение пути к скрипту.Пожалуйста, обратитесь к Дополнительная информация приведена в разделе "ДРУГИЕ ФУНКЦИИ СКРИПТА" на этой странице.

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

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

Недавно я столкнулся с такой же проблемой.Следующая статья помогла мне решить эту проблему: 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

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

$MyInvocation.MyCommand.Path

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

Cédric

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

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

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

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

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

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

CommandType     Name                                               ModuleName                                                                                                                                                
-----------     ----                                               ----------                                                                                                                                                
Function        Get-ScriptDirectory                                MyModule  
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top