Як отримати поточний каталог виконуваного командлету


202

Це повинно бути простим завданням, але я бачив кілька спроб, як отримати шлях до каталогу, де виконується командлет зі змішаним успіхом. Наприклад, коли я виконую 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?


11
Зауважте, що неоднозначна назва цього питання призвела до того, що відповіді нижче вирішили одну з двох різних проблем, не чітко вказуючи, яка: (a) як посилатися на поточне місцеположення (каталог) або (b) як посилатися на місце запущеного сценарію ( каталог, в якому знаходиться запущений скрипт, який може бути, а може і не бути поточним каталогом).
mklement0

Відповіді:


143

Надійний спосіб зробити це так, як ви показали $MyInvocation.MyCommand.Path.

Використання відносних шляхів буде засновано на $ pwd, в PowerShell, поточному каталозі програми або поточному робочому каталозі для API API .NET.

PowerShell v3 + :

Використовуйте автоматичну змінну $PSScriptRoot.


6
Чи можете ви поясніть мені, як ви знайшли власність PATH? $ MyInvocation.MyCommand | gm не показує таке властивість у списку учасників.
Віталій Маркітанов

19
чому б просто не використовувати $ PSScriptRoot?
Надійніше

@ user2326106 Чи можете ви пояснити різницю між $PSScriptRootі $MyInvocation.MyCommand.Path?
duct_tape_coder

1
Ця відповідь неповна.
K - Токсичність в SO зростає.

Ця відповідь неповна.
stackleit

263

Так, це має спрацювати. Але якщо вам потрібно побачити абсолютний шлях, це все, що вам потрібно:

(Get-Item -Path ".\").FullName

4
Дякую, це чудовий метод знайти повний шлях від відносних шляхів. Наприклад (Get-Item -Path $ myRelativePath -Verbose) .FullName
dlux

Дякую за це. Інші відповіді не працювали для сценаріїв Powershell, складених до EXE.
Зак Олександр

10
Це неправильно . Це отримує поточний каталог процесу , який може бути де завгодно. Наприклад, якщо поточний каталог мого командного рядка є C:\mydir, і я викликаю команду C:\dir1\dir2\dir3\mycmdlet.ps1, то це буде вирішено C:\mydir, а не C:\dir1\dir2\dir3. Викликати новий виконуваний файл має ту саму проблему, оскільки поточний каталог успадковується від батьківського процесу.
jpmc26

85

Найпростішим методом, здається, є використання наступної визначеної змінної:

 $PSScriptRoot

about_Automatic_Variablesі about_Scriptsобидва штати:

У PowerShell 2.0 ця змінна діє лише в модулях скриптів (.psm1). Починаючи з PowerShell 3.0, він дійсний у всіх сценаріях.

Я використовую це так:

 $MyFileName = "data.txt"
 $filebase = Join-Path $PSScriptRoot $MyFileName

12
Це конкретна версія. Для цього потрібно як мінімум Powershell 3.0.
Марвін Дікгаус

1
Це те, що мені потрібно було посилати на файл у тому самому місці, що і сценарій - дякую!
Адам Прескотт

Це найкраща відповідь, оскільки він дає точний шлях, де ви знаходитесь скрипт PS, який по суті є коренем для виконання вашого сценарію. Не байдуже, що це ваш поточний робочий каталог, звідки ви посилалися на свої сценарії. +1.
RBT

@MarvinDickhaus Ось чому для більшості сценаріїв потрібно використовувати "Set-StrictMode -Version 3.0" :) Велике спасибі за посилання!
Олександр Шапкін

44

Ви також можете використовувати:

(Resolve-Path .\).Path

Частина в дужках повертає PathInfoоб’єкт.

(Доступно з PowerShell 2.0.)


2
Це неправильно . Це отримує поточний каталог процесу , який може бути де завгодно. Наприклад, якщо поточний каталог мого командного рядка є C:\mydir, і я викликаю команду C:\dir1\dir2\dir3\mycmdlet.ps1, то це буде вирішено C:\mydir, а не C:\dir1\dir2\dir3. Викликати новий виконуваний файл має ту саму проблему, оскільки поточний каталог успадковується від батьківського процесу.
jpmc26

3
Дякую! Я також неправильно зрозумів назву цього питання, і ця відповідь була саме тим, що я шукав. Однак ... Це не відповідає на питання.
Райан Ліч

33

Шлях часто є нульовим. Ця функція безпечніша.

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("\"));
    }
}

1
чому -Освіт 1? не -Скоп 0
suiwenfeng

1
Змінна Get: Змінна область "1" перевищує кількість активних областей.
suiwenfeng

2
Ви отримуєте цю помилку, оскільки у вас немає сфери використання батьків. Параметр -Scope отримує змінну в заданій області. 1 у цьому випадку є батьківською сферою. Для отримання додаткової інформації дивіться цю статтю Technet про Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Christian Flem

27

Спробуйте:

(Get-Location).path

або:

($pwd).path

Я постійно забуваю про $pwd/ $PWDщоразу, коли я роблю довгу перерву від powerhell! Так набагато корисніше IMO ...
kayleeFrye_onDeck




4

У Powershell 3 і вище ви можете просто використовувати

$PSScriptRoot


1

Ви можете подумати, що використання ". \" Як шляху означає, що це шлях виклику. Але не весь час. Наприклад, якщо ви використовуєте його всередині завдання ScriptBlock. У такому випадку це може вказувати на% profile% \ Documents.


1

Ця функція буде встановлювати швидке розташування до шляху скрипта, розглядаючи спосіб різного способу проходження сценарію між 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 }
}

0

Для того, що варто, щоб бути однолінійним рішенням, нижче для мене є робочим рішенням.

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

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

Завдяки публікаціям вище, використовуючи командлет Get-Location .


0

Більшість відповідей не спрацьовує під час налагодження в таких IDE:

  • PS-ISE (PowerShell ISE)
  • Код VS (Код візуальної студії)

Тому що в тих, що $PSScriptRootпорожні, і Resolve-Path .\(і подібні зображення) це призведе до неправильних шляхів.

Відповідь Freakydinde - це єдиний, який вирішує ці ситуації, тому я це проголосував, але я не думаю, Set-Locationщо відповідь у цій відповіді є дійсно тим, що бажано. Тому я виправив це і зробив код трохи зрозумілішим:

$directorypath = if ($PSScriptRoot) { $PSScriptRoot } `
    elseif ($psise) { $psise.CurrentFile.FullPath } `
    elseif ($psEditor) { split-path $psEditor.GetEditorContext().CurrentFile.Path }

-1

Для розширення відповіді на @Cradle: ви також можете написати багатоцільову функцію, яка отримає той самий результат по питанню ОП:

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
    }

}

-1

Якщо вам просто потрібна назва поточного каталогу, ви можете зробити щось подібне:

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

Якщо припустимо, що ви працюєте з C: \ Temp \ Location \ MyWorkingDirectory>

Вихідні дані

MyWorkingDirectory


-1

У мене виникли подібні проблеми, і це створило мені багато проблем, оскільки я створюю програми, написані в PowerShell (повне програмне забезпечення для користувачів GUI), і у мене є багато файлів і ресурсів, які мені потрібно завантажити з диска. З мого досвіду, використовуючи. для представлення поточного каталогу ненадійно. Він повинен представляти поточний робочий каталог, але часто це не так. Здається, що PowerShell зберігає місце, звідки PowerShell викликався всередині. . Якщо бути точнішим, коли PowerShell вперше запущений, він за замовчуванням запускається всередині вашого домашнього каталогу користувачів. Це зазвичай каталог вашого облікового запису користувача, щось подібнеC:\USERS\YOUR USER NAME. Після цього PowerShell змінює каталог до каталогу, з якого ви його викликали, або до каталогу, де знаходиться сценарій, який ви виконуєте, перед тим, як представити вам запит PowerShell або запустити сценарій. Але це відбувається після того, як саме додаток PowerShell спочатку запускається в домашній каталог користувачів.

І .являє собою початковий каталог, всередині якого запущений PowerShell. Тож .представляє поточний каталог лише в тому випадку, якщо ви посилалися на PowerShell з потрібного каталогу. Якщо пізніше ви зміните каталог у коді PowerShell, здається, що зміни не відображаються всередині .кожного. В одних випадках .представлений поточний робочий каталог, а в інших каталог, з якого викликано PowerShell (сам, а не сценарій), що може призвести до непослідовних результатів. З цієї причини я використовую скрипт провізника. Сценарій PowerShell з однією командою всередині: представляють поточну каталог. Але це працює лише в тому випадку, якщо ви згодом не зміните каталог у PowerShell-коді. У випадку скрипту я використовую скрипт-визыватель, подібний до останнього, про який я згадав, за винятком того, що він містить варіант файлу: POWERSHELL . Це забезпечить виклик PowerShell з потрібного каталогу і таким чином зробити.POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Це забезпечує запуск PowerShell всередині поточного робочого каталогу.

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

Але, щоб уникнути всієї суєти та використання скрипта провадника, ви можете просто використовувати $PWDабо $PSSCRIPTROOTзамість того, .щоб представляти поточний каталог залежно від погоди, яку ви хочете представляти поточний робочий каталог або каталог, з якого виклик сценарію. І якщо ви з якоїсь причини хочете отримати інші два каталоги, які .повертаються, ви можете використовувати $HOME.

Я особисто просто маю скрипт провізора у кореневому каталозі своїх додатків, які я розробляю за допомогою PowerShell, який викликає мій основний сценарій програми, і просто пам'ятаю, щоб ніколи не змінювати поточний робочий каталог всередині мого вихідного коду свого додатка, тому мені ніколи про це не потрібно хвилюватися, і я можу використовувати, .щоб представляти поточний каталог і підтримувати відносну адресування файлів у своїх програмах без проблем. Це повинно працювати в нових версіях PowerShell (новіших, ніж версія 2).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.