Geçerli PowerShell betiğinin konumunu belirlemenin en iyi yolu nedir?


530

Ortak bir modüle veya komut dosyasına başvurmam gerektiğinde, geçerli komut dosyasına göre yolları kullanmayı seviyorum. Bu şekilde, komut dosyam her zaman kütüphanede başka komut dosyaları bulabilir.

Peki, mevcut komut dosyasının dizinini belirlemenin en iyi, standart yolu nedir? Şu anda yapıyorum:

$MyDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)

Modüllerde (.psm1) $PSScriptRootbu bilgiyi almak için kullanabileceğinizi biliyorum , ancak bu normal komut dosyalarında (yani .ps1 dosyaları) ayarlanmıyor.

Geçerli PowerShell komut dosyasının konumunu almanın standart yolu nedir?


Yanıtlar:


865

PowerShell 3+

# This is an automatic variable set to the current file's/module's directory
$PSScriptRoot

PowerShell 2

PowerShell 3'ten önce, MyInvocation.MyCommand.Definitionözelliği genel komut dosyaları için sorgulamaktan daha iyi bir yol yoktu . Ben esasen sahip olduğum her PowerShell betiğinin üstünde aşağıdaki satırı vardı:

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition

1
Split-PathBurada ne için kullanılır?
CMCDragonkai

7
Split-Path-Parentşu anda yürütülen komut dosyasının adı olmadan geçerli dizini döndürmek için parametre ile birlikte kullanılır .
hjoelr

5
Not: Linux / macOS üzerinde PowerShell ile, PSScriptRoot / MyInvocation vb. Dosyalarının doldurulması için komut dosyanızın .ps1 uzantısı olmalıdır. Buradaki hata raporuna bakın: github.com/PowerShell/PowerShell/issues/4217
Dave Wood

3
Potansiyel olarak ilginç bir kenara çekiliş: Bir komut dosyasının üst düzey kapsamında aynı davranıyor olsalar da (bu amaç için çağrılacak tek mantıklı yer ) , v2'nin yakın v2 yaklaşımı $PSScriptRoot( Split-Path -Parentuygulanır) $MyInvocation.MyCommand.Pathdeğil $MyInvocation.MyCommand.Definition. Bir işlev veya komut dosyası bloğunun içine çağrıldığında , ilkinde boş dize döndürülürken, ikincisi işlev gövdesinin / komut dosyası bloğunun tanımını dize (bir PowerShell kaynak kodu parçası) olarak döndürür.
mklement0

62

Bir V2 Modülü oluşturuyorsanız, adlı otomatik bir değişken kullanabilirsiniz $PSScriptRoot.

PS'den> Yardım auto_variable

$ PSScriptRoot
       Komut dosyası modülünün yürütüldüğü dizini içerir.
       Bu değişken, komut dosyalarının diğerlerine erişmek için modül yolunu kullanmasına izin verir
       kaynaklar.

16
PS 3.0'da ihtiyaç duyduğunuz şey budur:$PSCommandPath Contains the full path and file name of the script that is being run. This variable is valid in all scripts.
CodeMonkeyKing

2
Sadece $ PSScriptRoot test edildi ve beklendiği gibi çalışıyor. Ancak, komut satırında çalıştırırsanız size boş bir dize verir. Yalnızca bir komut dosyasında kullanılır ve komut dosyası yürütülürse sonuç verir. Bunun için kastedilen .....
Farrukh Waheed

4
Kafam karıştı. Bu cevap V2 için PSScriptRoot kullandığını söylüyor. Başka bir cevap, PSScriptRoot'un V3 + için olduğunu ve v2 için farklı bir şey kullanmak olduğunu söylüyor.

6
v2'deki @user $ PSScriptRoot sadece modüller içindir , eğer modülde olmayan 'normal' komut dosyaları yazıyorsanız, $ MyInvocation.MyCommand.Definition'a ihtiyacınız var, üst cevaba bakınız.
yzorg

1
@Lofful "v2" de sadece modüller için tanımlandığını söyledim. V3'teki modüllerin dışında tanımlandığını söylüyorsunuz. Sanırım aynı şeyi söylüyoruz. :)
yzorg

35

PowerShell 3.0 için

$PSCommandPath
    Contains the full path and file name of the script that is being run. 
    This variable is valid in all scripts.

Bu durumda işlev:

function Get-ScriptDirectory {
    Split-Path -Parent $PSCommandPath
}

20
Daha da iyisi, $ PSScriptRoot kullanın. Geçerli dosyanın / modülün dizinidir.
Aaron Jensen

2
Bu komut, bunu fark edene kadar beni fırlatan komut dosyasının dosya adını içerir. Yolu istediğinizde, komut dosyası adının da orada olmasını istemezsiniz. En azından, bunu isteyebileceğin bir sebep düşünemiyorum. $ PSScriptRoot dosya adını içermez (diğer yanıtlardan temizlenir).
YetAnotherRandomUser

$ PSScriptRoot normal bir PS1 betiğinden boş. $ PSCommandPath olsa da çalışır. Diğer yazılarda verilen açıklamalara göre her ikisinin de davranışı beklenmektedir. Dosya adı olmadan komut dosyası dizinini almak için sadece [IO.Path] :: GetDirectoryName ($ PSCommandPath) kullanabilirsiniz.
Nisan'da

19

PowerShell 3+ için

function Get-ScriptDirectory {
    if ($psise) {
        Split-Path $psise.CurrentFile.FullPath
    }
    else {
        $global:PSScriptRoot
    }
}

Bu işlevi profilime yerleştirdim. F8Seçimi / Çalıştır seçeneğini kullanarak İMKB'de de çalışır .


16

Belki burada bir şey eksik ... ama mevcut çalışma dizini istiyorsanız sadece bunu kullanabilirsiniz: (Get-Location).Pathbir dize veya Get-Locationbir nesne için.

Soruyu tekrar okuduktan sonra anladığım böyle bir şeye atıfta bulunmadıkça.

function Get-Script-Directory
{
    $scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
    return Split-Path $scriptInvocation.MyCommand.Path
}

16
Bu , kullanıcının komut dosyasını çalıştırdığı geçerli konumu alır . Komut dosyasının kendisi değil .
Aaron Jensen

2
function Get-Script-Directory { $scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value return Split-Path $scriptInvocation.MyCommand.Path } $hello = "hello" Write-Host (Get-Script-Directory) Write-Host $hello Bunu kaydedin ve farklı bir dizinden çalıştırın. Komut dosyasının yolunu göstereceksiniz.
Sean

Bu iyi bir işlev ve ihtiyacım olanı yapıyor, ancak bunu tüm komut dosyalarında nasıl paylaşabilirim ve kullanabilirim? Bu bir tavuk ve yumurta sorunu: Mevcut konumumu bulmak için bir işlev kullanmak istiyorum, ancak işlevi yüklemek için konumuma ihtiyacım var.
Aaron Jensen

2
NOT: Bu işlevin çağrılması, başka bir işlev içinde yuvalanmışsa, komut dosyanızın en üst düzeyinde olmalıdır, o zaman çağrı yığınında ne kadar derin olduğunuzu belirtmek için "-Scope" parametresini değiştirmeniz gerekir.
kenny

11

Önceden gönderilen cevaplara çok benzer, ancak borular daha fazla PowerShell benzeri görünüyor:

$PSCommandPath | Split-Path -Parent

11

Kullandığım otomatik değişken $ExecutionContext . PowerShell 2 ve sonrasında çalışır.

 $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath('.\')

$ ExecutionContext Windows PowerShell ana bilgisayarının yürütme bağlamını temsil eden bir EngineIntrinsics nesnesi içerir. Cmdlet'lerin kullanabileceği yürütme nesnelerini bulmak için bu değişkeni kullanabilirsiniz.


1
Bu benim için çalıştı, STDIN'den powershell beslemeye çalıştı.
Sebastian

Bu çözüm, UNC yolu bağlamındayken de düzgün çalışır.
user2030503

1
Bu görünüşe göre komut dosyasının bulunduğu yer yerine çalışma dizinini alıyor?
monojohnny

@monojohnny Evet, bu temel olarak geçerli çalışma dizinidir ve başka bir konumdan komut dosyası çağırırken çalışmaz.
Marsze

9

Kabul edilen yanıtı alan ve sağlam bir işleve dönüştüren bir şey geliştirmek benim için biraz zaman aldı.

Diğerleri hakkında emin değilim, ama hem PowerShell sürüm 2 hem de 3'teki makinelerle bir ortamda çalışıyorum, bu yüzden her ikisini de ele almam gerekiyordu. Aşağıdaki işlev zarif bir geri dönüş sunar:

Function Get-PSScriptRoot
{
    $ScriptRoot = ""

    Try
    {
        $ScriptRoot = Get-Variable -Name PSScriptRoot -ValueOnly -ErrorAction Stop
    }
    Catch
    {
        $ScriptRoot = Split-Path $script:MyInvocation.MyCommand.Path
    }

    Write-Output $ScriptRoot
}

Ayrıca, işlevin, Michael Sorens tarafından blog yayınlarından birinde ana hatlarıyla belirtilen Komut kapsamına değil, Komut Dosyası kapsamına atıfta bulunduğu anlamına gelir .


Teşekkürler! "$ Script:" Windows PowerShell ISE çalışmak için gereken bu oldu.
Kirk Liemohn

Bunun için teşekkürler. Benim için çalışan tek kişi buydu. Ben genellikle Get-location gibi şeyler benim için işe başlamadan önce komut dosyası içinde dizin içine CD gerekir. PowerShell'in dizini neden otomatik olarak güncellemediğini bilmek isterim.
Zain

7

Komut dosyası adını ve nereden yürütüldüğünü bilmem gerekiyordu.

MyInvocation yapısına "$ global:" ön eki, hem ana komut dosyasından hem de içe aktarılan bir .PSM1 kitaplık dosyasının ana satırından çağrıldığında tam yolu ve komut dosyası adını döndürür. Ayrıca, içe aktarılan bir kitaplıktaki bir işlev içinden de çalışır.

Etrafta çok uğraştıktan sonra $ global kullanmaya karar verdim: MyInvocation.InvocationName. CMD lansmanı, Run With Powershell ve İMKB ile güvenilir bir şekilde çalışır. Hem yerel hem de UNC lansmanları doğru yolu döndürür.


4
Split-Path -Path $ ($ global: MyInvocation.MyCommand.Path) mükemmel çalıştı. Diğer çözümler çağıran uygulamanın yolunu döndürdü.
dynamiclynk

1
Önemsiz not: ISE'de bu işlevi F8 / Run Selection kullanarak çağırmak bir ParameterArgumentValidationErrorNullNotAllowedistisnayı tetikleyecektir .
savak

5

Her zaman PowerShell ve ISE için aynı şekilde çalışan bu küçük snippet'i kullanıyorum :

# Set active path to script-location:
$path = $MyInvocation.MyCommand.Path
if (!$path) {
    $path = $psISE.CurrentFile.Fullpath
}
if ($path) {
    $path = Split-Path $path -Parent
}
Set-Location $path

3

Burada yayınlanan eski çözümlerin PowerShell V5'te benim için çalışmadığını gördüm. Ben bununla geldim:

try {
    $scriptPath = $PSScriptRoot
    if (!$scriptPath)
    {
        if ($psISE)
        {
            $scriptPath = Split-Path -Parent -Path $psISE.CurrentFile.FullPath
        }
        else {
            Write-Host -ForegroundColor Red "Cannot resolve script file's path"
            exit 1
        }
    }
}
catch {
    Write-Host -ForegroundColor Red "Caught Exception: $($Error[0].Exception.Message)"
    exit 2
}

Write-Host "Path: $scriptPath"

2

split-path -parent $psISE.CurrentFile.FullpathDiğer yöntemlerden herhangi birinin başarısız olup olmadığını da düşünebilirsiniz . Özellikle, bir grup işlevi yüklemek için bir dosya çalıştırırsanız ve daha sonra bu işlevleri İMKB kabuğuyla yürütürseniz (veya çalıştırmayı seçtiyseniz), Get-Script-Directoryyukarıdaki gibi çalışmaz.


3
$PSCommandPathkomut dosyasını kaydettiğiniz ve tüm dosyayı yürüttüğünüz sürece İMKB'de çalışacaktır. Aksi takdirde, aslında bir komut dosyası çalıştırmazsınız; sadece komutları kabuğa "yapıştırabilirsiniz".
Zenexer

@Zenexer Bence o zamanlar hedefim buydu.

2

Tüm bu cevaplardan ve yorumlardan parçalar kullanarak, bu soruyu gelecekte görecek herkes için bir araya getirdim. Diğer cevaplarda listelenen tüm durumları kapsar

    # If using ISE
    if ($psISE) {
        $ScriptPath = Split-Path -Parent $psISE.CurrentFile.FullPath
    # If Using PowerShell 3 or greater
    } elseif($PSVersionTable.PSVersion.Major -gt 3) {
        $ScriptPath = $PSScriptRoot
    # If using PowerShell 2 or lower
    } else {
        $ScriptPath = split-path -parent $MyInvocation.MyCommand.Path
    }

-4
function func1() 
{
   $inv = (Get-Variable MyInvocation -Scope 1).Value
   #$inv.MyCommand | Format-List *   
   $Path1 = Split-Path $inv.scriptname
   Write-Host $Path1
}

function Main()
{
    func1
}

Main
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.