Как получить текущий каталог выполняемого командлета

Это должна быть простая задача, но я видел несколько попыток получить путь к каталогу, в котором находится исполняемый командлет, с переменным успехом. Например, когда я выполняю C:\temp\myscripts\mycmdlet.ps1, у которого есть файл настроек в C:\temp\myscripts\settings.xml, я хотел бы иметь возможность сохранить C:\temp\myscripts в переменной внутри mycmdlet.ps1.

Это одно из решений, которое работает (хотя и немного громоздко):

$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$settingspath = $directorypath + '\settings.xml'

Другой предложил это решение, которое работает только в нашей тестовой среде:

$settingspath = '.\settings.xml'

Мне очень нравится последний подход, и я предпочитаю его каждый раз анализировать путь к файлу как параметр, но я не могу заставить его работать в моей среде разработки. Что мне делать? Это как-то связано с настройкой PowerShell?


person Stig Perez    schedule 01.12.2011    source источник
comment
Обратите внимание, что неоднозначное название этого вопроса привело к приведенным ниже ответам, решающим одну из двух различных проблем, без явного указания: (а) как ссылаться на текущее местоположение (каталог) или < / i> (b) как ссылаться на местоположение запущенного скрипта (каталог, в котором находится запущенный скрипт, который может быть или не быть текущим каталогом).   -  person mklement0    schedule 24.01.2018
comment
Возможный дубликат Что лучше как определить местонахождение текущего скрипта PowerShell?   -  person jpmc26    schedule 14.04.2018


Ответы (18)


Надежный способ сделать это такой же, как вы показали $MyInvocation.MyCommand.Path.

Использование относительных путей будет основано на $ pwd в PowerShell, текущем каталоге для приложения или текущем рабочем каталоге для .NET API.

PowerShell v3 +:

Используйте автоматическую переменную $PSScriptRoot.

person JasonMArcher    schedule 06.12.2011
comment
Не могли бы вы объяснить мне, как вы нашли свойство PATH? $ MyInvocation.MyCommand | gm не показывает такое свойство в списке участников. - person Vitaliy Markitanov; 11.12.2016
comment
почему бы просто не использовать $ PSScriptRoot? Кажется более надежным - person mBrice1024; 28.07.2017
comment
@ user2326106 Вы можете объяснить разницу между $PSScriptRoot и $MyInvocation.MyCommand.Path? - person duct_tape_coder; 14.06.2019
comment
@VitaliyMarkitanov Вы запускали $MyInvocation.MyCommand | gm в скрипте? Path относится к полному пути к файлу сценария, поэтому вы не найдете его, выполнив непосредственно в терминале. - person Connor Low; 29.04.2020
comment
У меня были проблемы с $MyInvocation.MyCommand.Path\File.csv и после того, как я погуглил $MyInvocation.MyCommand.Path, я обнаружил это статья MS, в которой подробно рассказывается, что мне пришлось отделить часть этого и вставить переменную, прежде чем я смог заставить ее работать: $myDir = Split-Path -Parent $MyInvocation.MyCommand.Path. ОЧЕНЬ новичок в PowerShell, поэтому проблема, вероятно, связана с моим непониманием - person gregg; 20.10.2020

Да, это должно сработать. Но если вам нужно увидеть абсолютный путь, это все, что вам нужно:

(Get-Item .).FullName
person GuruKay    schedule 17.04.2014
comment
Спасибо, это отличный способ найти полный путь из относительных путей. Например. (Get-Item -Path $ myRelativePath -Verbose) .FullName - person dlux; 06.05.2014
comment
Спасибо тебе за это. Другие ответы не работали для скриптов Powershell, скомпилированных в EXE. - person Zach Alexander; 03.11.2017
comment
Это неправильно. Это получает текущий каталог процесса, который может быть где угодно. Например, если в моей командной строке текущий каталог C:\mydir, и я вызываю команду C:\dir1\dir2\dir3\mycmdlet.ps1, тогда это будет C:\mydir, а не C:\dir1\dir2\dir3. При вызове нового исполняемого файла возникает та же проблема, поскольку текущий каталог наследуется от родительского процесса. - person jpmc26; 14.04.2018
comment
Работал как шарм - person Aquaphor; 25.05.2021

Самый простой способ - использовать следующую предопределенную переменную:

 $PSScriptRoot

about_Automatic_Variables и _ 3_ оба состояния:

В PowerShell 2.0 эта переменная действительна только в модулях сценария (.psm1). Начиная с PowerShell 3.0, он действует во всех скриптах.

Я использую это так:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName
person Tony Sheen    schedule 24.09.2014
comment
Это зависит от версии. Для этого требуется как минимум Powershell 3.0. - person Marvin Dickhaus; 10.04.2015
comment
Это то, что мне нужно для ссылки на файл в том же месте, что и сценарий - спасибо! - person Adam Prescott; 21.11.2017
comment
Это лучший ответ, поскольку он дает вам именно тот путь, по которому присутствует ваш скрипт PS, который, по сути, является корнем для выполнения вашего скрипта. Неважно, какой у вас текущий рабочий каталог, из которого вы вызывали свои скрипты. +1. - person RBT; 17.05.2018
comment
@MarvinDickhaus Вот почему необходимо использовать Set-StrictMode -Version 3.0 в большинстве ваших скриптов :) Большое спасибо за ссылки! - person Alexander Shapkin; 28.05.2018

Вы также можете использовать:

(Resolve-Path .\).Path

Часть в скобках возвращает объект PathInfo.

(Доступно, начиная с PowerShell 2.0.)

person Alex Angas    schedule 27.05.2015
comment
Это неправильно. Это получает текущий каталог процесса, который может быть где угодно. Например, если текущий каталог моей командной строки - C:\mydir, и я вызываю команду C:\dir1\dir2\dir3\mycmdlet.ps1, тогда это будет C:\mydir, а не C:\dir1\dir2\dir3. При вызове нового исполняемого файла возникает та же проблема, поскольку текущий каталог наследуется от родительского процесса. - person jpmc26; 14.04.2018
comment
Спасибо! Я также неправильно понял название этого вопроса, и этот ответ был именно тем, что я искал. Однако ... Это не отвечает на вопрос. - person Ryan The Leach; 28.06.2018

Пытаться :

(Get-Location).path

or:

($pwd).path
person Rohin Sidharth    schedule 10.01.2016
comment
Я все время забываю о _1 _ / _ 2_, когда делаю долгий перерыв в PowerShell! ИМО гораздо полезнее ... - person kayleeFrye_onDeck; 17.05.2019

Путь часто равен нулю. Эта функция более безопасна.

function Get-ScriptDirectory
{
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value;
    if($Invocation.PSScriptRoot)
    {
        $Invocation.PSScriptRoot;
    }
    Elseif($Invocation.MyCommand.Path)
    {
        Split-Path $Invocation.MyCommand.Path
    }
    else
    {
        $Invocation.InvocationName.Substring(0,$Invocation.InvocationName.LastIndexOf("\"));
    }
}
person Christian Flem    schedule 23.10.2013
comment
почему -Сфера 1? not -Scope 0 - person suiwenfeng; 19.01.2016
comment
Get-Variable: номер области «1» превышает количество активных областей. - person suiwenfeng; 19.01.2016
comment
Вы получаете эту ошибку, потому что у вас нет родительской области. -Scope параметр получает переменную в указанной области. 1 в данном случае является родительской областью. Для получения дополнительной информации см. Эту техническую статью о Get-Variable (technet.microsoft.com/ en-us / library / hh849899.aspx) - person Christian Flem; 20.01.2016

Get-Location вернет текущий место нахождения:

$Currentlocation = Get-Location
person Veera Induvasi    schedule 09.03.2017
comment
PS C: \ Windows \ system32 ›C: \ powershell \ checkfile.ps1 -› это даст c: \ windows \ system32 - person nbi; 10.04.2017

Мне нравится однострочное решение :)

$scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
person FrankyHollywood    schedule 17.12.2015
comment
Да работает у меня. Мой pwd - это мой каталог пользователя, и я запускаю сценарий в другом каталоге (B), который он дает (B) - person andrew pate; 06.04.2018

Попробуй это:

$WorkingDir = Convert-Path .
person Sevenfold Exurbanite    schedule 21.06.2017

В Powershell 3 и выше вы можете просто использовать

$PSScriptRoot

person BlackSpy    schedule 03.05.2018

Большинство ответов не работают при отладке в следующих IDE:

  • PS-ISE (PowerShell ISE)
  • VS Code (код Visual Studio)

Потому что в них $PSScriptRoot пусто, а Resolve-Path .\ (и аналогичные) приведет к неправильным путям.

Ответ Freakydinde - единственный, который разрешает эти ситуации, поэтому я проголосовал за него, но не думаю, что Set-Location в этом ответе действительно то, что нужно. Я исправил это и сделал код немного понятнее:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { split-path $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }
person Mariano Desanze    schedule 27.06.2020
comment
Я обнаружил, что это единственный вариант, который работает как в PowerShell ISE, так и в VS Code. - person Karson; 17.05.2021

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

((Get-Location) | Get-Item).Name

Предполагая, что вы работаете из C: \ Temp \ Location \ MyWorkingDirectory>

Вывод

MyWorkingDirectory

person SolThoth    schedule 12.09.2019

Как бы то ни было, чтобы быть однострочным решением, приведенное ниже решение для меня является рабочим.

$currFolderName = (Get-Location).Path.Substring((Get-Location).Path.LastIndexOf("\")+1)

1 в конце - игнорировать /.

Благодаря сообщениям выше с использованием Get -Location.

person Ak777    schedule 02.03.2019

эта функция установит в качестве места приглашения путь к сценарию, имея дело с другим способом получения пути к сценарию между vscode, psise и pwd:

function Set-CurrentLocation
{
    $currentPath = $PSScriptRoot                                                                                                     # AzureDevOps, Powershell
    if (!$currentPath) { $currentPath = Split-Path $pseditor.GetEditorContext().CurrentFile.Path -ErrorAction SilentlyContinue }     # VSCode
    if (!$currentPath) { $currentPath = Split-Path $psISE.CurrentFile.FullPath -ErrorAction SilentlyContinue }                       # PsISE

    if ($currentPath) { Set-Location $currentPath }
}
person freakydinde    schedule 19.02.2020

Можно подумать, что использование '. \' В качестве пути означает, что это путь вызова. Но не всегда. Например, если вы используете его внутри задания ScriptBlock. В этом случае он может указывать на% profile% \ Documents.

person syanzy    schedule 06.12.2013

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

@((
  ($MyInvocation.MyCommand.Module.ModuleBase),
  ($PSScriptRoot),
  (Split-Path -Parent -Path $MyInvocation.MyCommand.Definition -ErrorAction SilentlyContinue),
  (Get-Location | Select-Object -ExpandProperty Path)
) | Where-Object { $_ })[0]
person zman    schedule 16.07.2021

Чтобы расширить ответ @Cradle: вы также можете написать многоцелевую функцию, которая даст вам тот же результат по вопросу OP:

Function Get-AbsolutePath {

    [CmdletBinding()]
    Param(
        [parameter(
            Mandatory=$false,
            ValueFromPipeline=$true
        )]
        [String]$relativePath=".\"
    )

    if (Test-Path -Path $relativePath) {
        return (Get-Item -Path $relativePath).FullName -replace "\\$", ""
    } else {
        Write-Error -Message "'$relativePath' is not a valid path" -ErrorId 1 -ErrorAction Stop
    }

}
person Erutan409    schedule 16.01.2016

У меня были аналогичные проблемы, и это доставляло мне много проблем, поскольку я делаю программы, написанные на PowerShell (приложения с полным графическим интерфейсом пользователя), и у меня есть много файлов и ресурсов, которые мне нужно загрузить с диска. По моему опыту, использование . для представления текущего каталога ненадежно. Он должен представлять текущий рабочий каталог, но часто это не так. Похоже, что PowerShell сохраняет место, из которого PowerShell был вызван внутри .. Если быть более точным, при первом запуске PowerShell по умолчанию запускается внутри вашего домашнего каталога пользователя. Обычно это каталог вашей учетной записи, например C:\USERS\YOUR USER NAME. После этого PowerShell изменяет каталог либо на каталог, из которого вы его вызывали, либо на каталог, в котором находится сценарий, который вы выполняете, перед тем, как либо представить вам приглашение PowerShell, либо запустить сценарий. Но это происходит после того, как само приложение PowerShell изначально запускается в вашем домашнем каталоге пользователя.

А . представляет тот исходный каталог, внутри которого запускается PowerShell. Таким образом, . представляет текущий каталог только в том случае, если вы вызвали PowerShell из нужного каталога. Если позже вы измените каталог в коде PowerShell, изменения не будут отражаться внутри . во всех случаях. В некоторых случаях . представляет текущий рабочий каталог, а в других - каталог, из которого был вызван PowerShell (сам по себе, а не скрипт), что может привести к противоречивым результатам. По этой причине я использую скрипт invoker. Сценарий PowerShell с одной командой внутри: POWERSHELL. Это гарантирует, что PowerShell будет вызван из желаемого каталога и, таким образом, . будет представлять текущий каталог. Но это работает только в том случае, если вы не измените каталог позже в коде PowerShell. В случае сценария я использую сценарий invoker, который похож на последний, который я упомянул, за исключением того, что он содержит параметр файла: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Это гарантирует, что PowerShell запускается внутри текущего рабочего каталога.

Простое нажатие на скрипт вызывает PowerShell из вашего домашнего каталога пользователя независимо от того, где находится скрипт. В результате текущий рабочий каталог является каталогом, в котором расположен скрипт, но каталог вызова PowerShell - C:\USERS\YOUR USER NAME, а . возвращает один из этих двух каталогов в зависимости от ситуации, что нелепо.

Но чтобы избежать всей этой суеты и использования сценария вызова, вы можете просто использовать $PWD или $PSSCRIPTROOT вместо . для представления текущего каталога в зависимости от погоды, в которой вы хотите представить текущий рабочий каталог или каталог, из которого был вызван сценарий. И если вы по какой-то причине хотите получить другой из двух каталогов, которые возвращает ., вы можете использовать $HOME.

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

person SYOB SYOT    schedule 03.11.2019