実行中のスクリプトがどのパスから実行されたかを確認できるようにしたいと考えています。
多くの場合、これは $pwd ではありません。
自分のスクリプトに関連するフォルダー構造にある他のスクリプトを呼び出す必要があり、パスをハードコーディングすることはできますが、「dev」から「test」に昇格しようとするとき、それは不快であり、首に少し苦痛です。 "製造"。
実行中のスクリプトがどのパスから実行されたかを確認できるようにしたいと考えています。
多くの場合、これは $pwd ではありません。
自分のスクリプトに関連するフォルダー構造にある他のスクリプトを呼び出す必要があり、パスをハードコーディングすることはできますが、「dev」から「test」に昇格しようとするとき、それは不快であり、首に少し苦痛です。 "製造"。
PowerShellチームのJeffreySnoverによって最初に投稿されたユビキタススクリプト( Skylerの回答で与えられた)とKeith CedircおよびEBGreenによって投稿されたバリエーションはすべて、深刻な欠点に悩まされています-コードが期待するものを報告するかどうかは、どこで呼び出すかによって異なります!
以下の私のコードは、親スコープの代わりにスクリプトスコープを参照するだけでこの問題を解決します。
function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
この問題を説明するために、4つの異なる方法でターゲット式を評価するテスト車両を作成しました。(括弧で囲まれた用語は、次の結果表のキーです。)
最後の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モジュールとカプセル化」で、モジュールに関する他のヒントやコツを使ってこれについて読むことができます。
$PSCommandPath
Powershell バージョン 1.0 の質問にタグを付けましたが、Powershell バージョン 3.0 にアクセスできる場合は$PSScriptRoot
、スクリプト パスを取得するのが少し簡単になります。詳細については、このページの「その他のスクリプト機能」セクションを参照してください。
数年間、ほとんどのスクリプトで次のようなコードを問題なく使用してきました。
#--------------------------------------------------------------------
# Dot source support scripts
#--------------------------------------------------------------------
$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir = Split-Path -Parent $ScriptPath
. $ScriptDir\BuildVars.ps1
. $ScriptDir\LibraryBuildUtils.ps1
. $ScriptDir\BuildReportUtils.ps1
私は最近同じ問題に遭遇しました。次の記事が問題の解決に役立ちました: 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
実行中のスクリプトのパスは、次を使用して見つけることができると思います
$MyInvocation.MyCommand.Path
それが役に立てば幸い !
セドリック
これは、PS の (少なくとも私の考えでは) 奇妙な点の 1 つです。それには完全に正当な理由があると確信していますが、それでも私には奇妙に思えます。そう:
スクリプト内にいて関数内にいない場合、$myInvocation.InvocationName はスクリプト名を含む完全なパスを提供します。スクリプトと関数内にいる場合、 $myInvocation.ScriptName は同じことを返します。
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