Cara mendapatkan direktori cmdlet saat ini sedang dieksekusi


202

Ini seharusnya menjadi tugas yang sederhana, tetapi saya telah melihat beberapa upaya tentang cara mendapatkan path ke direktori di mana cmdlet yang dieksekusi terletak dengan keberhasilan yang beragam. Misalnya, ketika saya mengeksekusi C:\temp\myscripts\mycmdlet.ps1yang memiliki file pengaturan diC:\temp\myscripts\settings.xml saya ingin dapat menyimpan C:\temp\myscriptsdalam variabel di dalamnya mycmdlet.ps1.

Ini adalah salah satu solusi yang berfungsi (walaupun agak rumit):

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

Yang lain menyarankan solusi ini yang hanya berfungsi pada lingkungan pengujian kami:

$settingspath = '.\settings.xml'

Saya suka pendekatan terakhir banyak dan lebih suka harus mengurai filepath sebagai parameter setiap kali, tapi saya tidak bisa membuatnya bekerja di lingkungan pengembangan saya. Apa yang harus saya lakukan? Apakah ada hubungannya dengan bagaimana PowerShell dikonfigurasi?


11
Perhatikan bahwa judul yang tidak jelas dari pertanyaan ini telah menghasilkan jawaban di bawah ini yang memecahkan salah satu dari dua masalah yang berbeda, tanpa secara eksplisit menyatakan: (a) bagaimana merujuk lokasi saat ini (direktori) atau (b) bagaimana cara merujuk lokasi skrip yang sedang berjalan ( direktori di mana skrip berjalan berada, yang mungkin atau mungkin bukan direktori saat ini).
mklement0

Jawaban:


143

Cara yang dapat diandalkan untuk melakukan ini sama seperti yang Anda tunjukkan $MyInvocation.MyCommand.Path.

Menggunakan jalur relatif akan didasarkan pada $ pwd, di PowerShell, direktori saat ini untuk aplikasi, atau direktori kerja saat ini untuk .NET API.

PowerShell v3 + :

Gunakan variabel otomatis $PSScriptRoot.


6
Bisakah Anda jelaskan bagaimana cara Anda menemukan PATH properti? $ MyInvocation.MyCommand | gm tidak menampilkan properti seperti itu di daftar anggota.
Vitaliy Markitanov

19
mengapa tidak menggunakan $ PSScriptRoot saja? Tampaknya lebih dapat diandalkan
mBrice1024

@ user2326106 Bisakah Anda menjelaskan perbedaan antara $PSScriptRootdan $MyInvocation.MyCommand.Path?
duct_tape_coder

1
Jawaban ini tidak lengkap.
K - Keracunan dalam SO tumbuh.

Jawaban ini tidak lengkap.
stackleit

263

Ya, itu seharusnya berhasil. Tetapi jika Anda perlu melihat jalur absolut, ini yang Anda butuhkan:

(Get-Item .).FullName

4
Terima kasih, ini adalah metode yang bagus untuk menemukan jalur lengkap dari jalur relatif. Misalnya (Dapatkan-Item -Path $ myRelativePath -Verbose) .FullName
dlux

Terima kasih untuk ini. Jawaban lain tidak berfungsi untuk skrip Powershell yang dikompilasi untuk EXEs.
Zach Alexander

10
Ini salah . Ini mendapatkan direktori saat ini dari proses , yang bisa di mana saja. Sebagai contoh, jika direktori saat ini baris perintah saya C:\mydir, dan saya memohon perintah C:\dir1\dir2\dir3\mycmdlet.ps1, maka ini akan menyelesaikan C:\mydir, bukan C:\dir1\dir2\dir3. Meminta executable baru memiliki masalah yang sama karena direktori saat ini diwarisi dari proses induk.
jpmc26

85

Metode termudah tampaknya menggunakan variabel yang telah ditentukan berikut ini:

 $PSScriptRoot

about_Automatic_Variablesdan about_Scriptskeduanya menyatakan:

Di PowerShell 2.0, variabel ini hanya valid dalam modul skrip (.psm1). Dimulai pada PowerShell 3.0, ini valid di semua skrip.

Saya menggunakannya seperti ini:

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

12
Ini adalah versi spesifik. Ini membutuhkan setidaknya Powershell 3.0.
Marvin Dickhaus

1
Inilah yang saya butuhkan untuk mereferensikan file di lokasi yang sama dengan skrip - terima kasih!
Adam Prescott

Ini adalah jawaban terbaik karena memberi Anda jalur yang tepat di mana skrip PS Anda hadir yang pada dasarnya adalah akar untuk eksekusi skrip Anda. Tidak peduli apa direktori pekerjaan Anda saat ini dari tempat Anda memanggil skrip Anda. +1.
RBT

@MarvinDickhaus Karena itulah diperlukan untuk menggunakan "Set-StrictMode -Version 3.0" di sebagian besar skrip Anda :) Terima kasih banyak untuk tautannya!
Alexander Shapkin

44

Anda juga bisa menggunakan:

(Resolve-Path .\).Path

Bagian dalam tanda kurung mengembalikan PathInfoobjek.

(Tersedia sejak PowerShell 2.0.)


2
Ini salah . Ini mendapatkan direktori saat ini dari proses , yang bisa di mana saja. Sebagai contoh, jika direktori saat ini baris perintah saya C:\mydir, dan saya memohon perintah C:\dir1\dir2\dir3\mycmdlet.ps1, maka ini akan menyelesaikan C:\mydir, bukan C:\dir1\dir2\dir3. Meminta executable baru memiliki masalah yang sama karena direktori saat ini diwarisi dari proses induk.
jpmc26

3
Terima kasih! Saya juga salah mengerti judul pertanyaan ini, dan jawaban ini persis apa yang saya cari. Namun ... Itu tidak menjawab pertanyaan.
Ryan The Leach

33

Path seringkali nol. Fungsi ini lebih aman.

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
mengapa -Scope 1? tidak -Scope 0
suiwenfeng

1
Get-Variable: Nomor lingkup '1' melebihi jumlah lingkup aktif.
suiwenfeng

2
Anda mendapatkan kesalahan ini karena Anda tidak memiliki ruang lingkup orang tua. Parameter -Scope mendapatkan variabel dalam lingkup yang ditentukan. 1 dalam hal ini adalah ruang lingkup induk. Untuk info lebih lanjut, lihat artikel technet ini tentang Get-Variable ( technet.microsoft.com/en-us/library/hh849899.aspx )
Christian Flem

27

Coba:

(Get-Location).path

atau:

($pwd).path

Saya selalu lupa tentang $pwd/ $PWDsetiap kali saya mengambil istirahat panjang dari PowerShell! IMO yang jauh lebih berguna ...
kayleeFrye_onDeck

17

Get-Location akan mengembalikan lokasi saat ini:

$Currentlocation = Get-Location

3
PS C: \ Windows \ system32> C: \ powershell \ checkfile.ps1 -> ini akan memberikan c: \ windows \ system32
nbi



4

Di Powershell 3 dan di atasnya Anda cukup menggunakan

$PSScriptRoot


1

Anda akan berpikir bahwa menggunakan '. \' Sebagai jalan berarti bahwa itu adalah jalan doa. Tapi tidak selalu. Contoh, jika Anda menggunakannya di dalam pekerjaan ScriptBlock. Dalam hal ini, ini mungkin mengarah ke% profile% \ Documents.


1

fungsi ini akan mengatur lokasi prompt ke jalur skrip, berurusan dengan cara berbeda untuk mendapatkan skrip antara vscode, psise dan 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

Untuk apa nilainya, untuk menjadi solusi single-line, di bawah ini adalah solusi yang berfungsi untuk saya.

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

1 di akhir adalah mengabaikan / .

Berkat tulisan di atas menggunakan cmdlet Get-Location .


0

Sebagian besar jawaban tidak berfungsi saat melakukan debug pada IDE berikut:

  • PS-ISE (PowerShell ISE)
  • VS Code (Visual Studio Code)

Karena pada mereka yang $PSScriptRootkosong dan Resolve-Path .\(dan serupa) akan menghasilkan jalur yang salah.

Jawaban Freakydinde adalah satu-satunya yang menyelesaikan situasi itu, jadi saya memilih itu, tetapi saya tidak berpikir Set-Locationbahwa jawaban itu adalah yang benar-benar diinginkan. Jadi saya memperbaikinya dan membuat kode sedikit lebih jelas:

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

-1

Untuk memperluas jawaban @Cradle: Anda juga bisa menulis fungsi multiguna yang akan memberi Anda hasil yang sama per pertanyaan 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
    }

}

-1

Jika Anda hanya perlu nama direktori saat ini, Anda dapat melakukan sesuatu seperti ini:

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

Dengan asumsi Anda bekerja dari C: \ Temp \ Location \ MyWorkingDirectory>

Keluaran

MyWorkingDirectory


-1

Saya memiliki masalah yang sama dan itu membuat saya banyak masalah karena saya membuat program yang ditulis dalam PowerShell (aplikasi GUI pengguna akhir penuh) dan saya memiliki banyak file dan sumber daya yang saya perlu memuat dari disk. Dari pengalaman saya, menggunakan . Setelah itu, PowerShell mengubah direktori menjadi direktori tempat Anda memintanya, atau ke direktori tempat skrip yang Anda jalankan berada sebelum menyajikan kepada Anda prompt PowerShell atau menjalankan skrip. Tapi itu terjadi setelah aplikasi PowerShell sendiri awalnya dimulai di dalam direktori pengguna rumah Anda.. untuk mewakili direktori saat ini tidak dapat diandalkan. Itu harus mewakili direktori kerja saat ini, tetapi seringkali tidak. Tampaknya PowerShell menyimpan lokasi dari mana PowerShell telah dipanggil di dalamnya .. Untuk lebih tepatnya, ketika PowerShell pertama kali dimulai, itu dimulai, secara default, di dalam direktori pengguna rumah Anda. Itu biasanya direktori akun pengguna Anda, kira-kira seperti ituC:\USERS\YOUR USER NAME

Dan .menyatakan bahwa direktori awal di mana PowerShell dimulai. Jadi .hanya mewakili direktori saat ini jika Anda menggunakan PowerShell dari direktori yang diinginkan. Jika nanti Anda mengubah direktori dalam kode PowerShell, perubahan tampaknya tidak tercermin di .dalam setiap kasus. Dalam beberapa kasus .merupakan direktori kerja saat ini, dan dalam direktori lain dari mana PowerShell (itu sendiri, bukan skrip) telah dipanggil, apa yang dapat menyebabkan hasil yang tidak konsisten. Untuk alasan ini saya menggunakan skrip invoker. Script PowerShell dengan single perintah di dalam: POWERSHELL. Itu akan memastikan bahwa PowerShell dipanggil dari direktori yang diinginkan dan karenanya dibuat.mewakili direktori saat ini. Tapi itu hanya berfungsi jika Anda tidak mengubah direktori nanti dalam kode PowerShell. Dalam kasus naskah, saya menggunakan script Invoker yang mirip dengan terakhir yang saya sebutkan, kecuali itu berisi file pilihan: POWERSHELL -FILE DRIVE:\PATH\SCRIPT NAME.PS1. Itu memastikan bahwa PowerShell dimulai di dalam direktori kerja saat ini.

Cukup dengan mengklik pada skrip, aktifkan PowerShell dari direktori pengguna rumah Anda di mana pun skrip berada. Ini hasil dengan direktori kerja saat ini menjadi direktori di mana skrip berada, tetapi direktori doa PowerShell sedang C:\USERS\YOUR USER NAME, dan dengan .mengembalikan salah satu dari dua direktori ini tergantung pada situasi, apa yang konyol.

Tetapi untuk menghindari semua keributan ini dan menggunakan skrip invoker, Anda bisa menggunakan salah satu $PWDatau $PSSCRIPTROOTalih-alih .merepresentasikan direktori saat ini tergantung pada cuaca yang ingin Anda tampilkan pada direktori atau direktori kerja saat ini dari mana skrip telah dipanggil. Dan jika Anda, karena suatu alasan, ingin mengambil yang lain dari dua direktori yang .kembali, Anda dapat menggunakannya $HOME.

Saya pribadi hanya memiliki skrip invoker di dalam direktori root aplikasi yang saya kembangkan dengan PowerShell yang memanggil skrip aplikasi utama saya, dan cukup ingat untuk tidak pernah mengubah direktori kerja saat ini di dalam kode sumber aplikasi saya, jadi saya tidak perlu khawatir tentang ini, dan saya bisa gunakan .untuk mewakili direktori saat ini dan untuk mendukung pengalamatan file relatif di aplikasi saya tanpa masalah. Ini harus bekerja di versi PowerShell yang lebih baru (lebih baru dari versi 2).

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.