64

実行中のスクリプトがどのパスから実行されたかを確認できるようにしたいと考えています。
多くの場合、これは $pwd ではありません。

自分のスクリプトに関連するフォルダー構造にある他のスクリプトを呼び出す必要があり、パスをハードコーディングすることはできますが、「dev」から「test」に昇格しようとするとき、それは不快であり、首に少し苦痛です。 "製造"。

4

7 に答える 7

101

PowerShellチームのJeffreySnoverによって最初に投稿されたユビキタススクリプト( Skylerの回答で与えられた)とKeith CedircおよびEBGreenによって投稿されたバリエーションはすべて、深刻な欠点に悩まされています-コードが期待するものを報告するかどうかは、どこで呼び出すかによって異なります!

以下の私のコードは、親スコープの代わりにスクリプトスコープを参照するだけでこの問題を解決します。

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

この問題を説明するために、4つの異なる方法でターゲット式を評価するテスト車両を作成しました。(括弧で囲まれた用語は、次の結果表のキーです。)

  1. インラインコード[インライン]
  2. インライン関数、つまりメインプログラムの関数[インライン関数]
  3. ドットソース関数、つまり同じ関数が別の.ps1ファイルに移動されました[ドットソース]
  4. モジュール関数、つまり同じ関数が別の.psm1ファイルに移動されました[モジュール]

最後の2列は、スクリプトスコープ(つまり$ script :)または親スコープ(-scope 1)を使用した結果を示しています。「script」の結果は、呼び出しがスクリプトの場所を正しく報告したことを意味します。「モジュール」の結果は、呼び出しが、関数を呼び出したスクリプトではなく、関数を含むモジュールの場所を報告したことを意味します。これは、関数をモジュールに入れることができないという両方の関数の欠点を示しています。

表からの注目すべき観察を脇に置いて、モジュールの問題を設定すると、親スコープのアプローチの使用はほとんどの場合失敗します(実際、成功の2倍の頻度で)。

入力の組み合わせの表

最後に、これがテスト車両です。

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で導入された内容についてはよくわかりませんが、Jeffrey Snoverが例を公開した時点で、PowerShell1にスクリプトスコープが存在していなかった可能性があります。

彼のコード例がWeb上で広く普及しているのに気付いたのに、試してみるとすぐに失敗したことに驚きました。しかし、それは、Snoverの例とは異なる方法で使用したためです(スクリプトトップではなく、別の関数の内部から呼び出しました(「2回ネストされた」例))。

2011.09.12更新

Simple-Talk.comで公開されたばかりの記事「RabbitHole のさらに下:PowerShellモジュールとカプセル化」で、モジュールに関する他のヒントやコツを使ってこれについて読むことができます。

于 2011-08-08T16:35:39.357 に答える
22

$PSCommandPathPowershell バージョン 1.0 の質問にタグを付けましたが、Powershell バージョン 3.0 にアクセスできる場合は$PSScriptRoot、スクリプト パスを取得するのが少し簡単になります。詳細については、このページの「その他のスクリプト機能」セクションを参照してください。

于 2013-10-09T23:44:38.280 に答える
11

数年間、ほとんどのスクリプトで次のようなコードを問題なく使用してきました。

#--------------------------------------------------------------------
# Dot source support scripts
#--------------------------------------------------------------------
$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir  = Split-Path -Parent $ScriptPath
. $ScriptDir\BuildVars.ps1
. $ScriptDir\LibraryBuildUtils.ps1
. $ScriptDir\BuildReportUtils.ps1
于 2009-05-01T18:21:01.010 に答える
6

私は最近同じ問題に遭遇しました。次の記事が問題の解決に役立ちました: 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
于 2009-04-29T16:58:57.700 に答える
5

実行中のスクリプトのパスは、次を使用して見つけることができると思います

$MyInvocation.MyCommand.Path

それが役に立てば幸い !

セドリック

于 2009-04-29T13:41:41.900 に答える
1

これは、PS の (少なくとも私の考えでは) 奇妙な点の 1 つです。それには完全に正当な理由があると確信していますが、それでも私には奇妙に思えます。そう:

スクリプト内にいて関数内にいない場合、$myInvocation.InvocationName はスクリプト名を含む完全なパスを提供します。スクリプトと関数内にいる場合、 $myInvocation.ScriptName は同じことを返します。

于 2009-04-29T13:34:21.450 に答える
0

msorensさん、ありがとうございます!これは、カスタム モジュールの作成に非常に役立ちました。誰かが自分で作ることに興味がある場合に備えて、私の構造は次のとおりです。

MyModule (folder)
 - MyModule.psd1 (help New-ModuleManifest)
 - MyScriptFile.ps1 (ps1 files are easy to test)

次に、MyModule.psd1 で MyScriptFile.ps1 を参照します。NestedModules 配列で .ps1 を参照すると、関数はグローバル セッション状態ではなく、モジュール セッション状態になります。(モジュールマニフェストの書き方)

NestedModules = @('.\MyScriptFile.ps1','.\MyOtherScriptFile.ps1')

MyScriptFile.ps1 の内容

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

try {
    Export-ModuleMember -Function "*-*"
}
catch{}

MyScriptFile.ps1 の実行時に、try/catch によって Export-ModuleMember からエラーが隠されます。

MyModuleディレクトリをここにあるパスの 1 つにコピーします$env:PSModulePath

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

CommandType     Name                                               ModuleName                                                                                                                                                
-----------     ----                                               ----------                                                                                                                                                
Function        Get-ScriptDirectory                                MyModule  
于 2013-01-30T21:27:37.297 に答える