web-dev-qa-db-ja.com

実行中のスクリプトのソースパスを見つけるにはどうすればよいですか?

実行中のスクリプトがどのパスから実行されたかを伝えたいと思います。
多くの場合、これは$ pwdではありません。

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

60
Kevin Buchan

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

以下の私のコードは、parentの代わりにscriptスコープを参照するだけでこの問題を解決します範囲:

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

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

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

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

モジュールの問題を別にすれば、テーブルからの注目すべき観察結果は、親スコープのアプローチを使用するとほとんどの場合失敗することです(実際には、それの2倍の頻度で成功)。

table of input combinations

最後に、テスト車両を示します。

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が例を公開した時点では、スクリプトスコープがPowerShell 1に存在していなかった可能性が非常に高いです。

彼のコード例がウェブ上で広範に増殖しているのを見つけたのに驚いたが、試してみるとすぐに失敗した!しかし、それはSnoverの例とは異なる方法で使用したためです(スクリプトトップではなく、別の関数の内部から呼び出しました(「ネストされた2回」の例))。

2011.09.12アップデート

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

96
Michael Sorens

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

20
Michael Kelley

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

#--------------------------------------------------------------------
# Dot source support scripts
#--------------------------------------------------------------------
$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir  = Split-Path -Parent $ScriptPath
. $ScriptDir\BuildVars.ps1
. $ScriptDir\LibraryBuildUtils.ps1
. $ScriptDir\BuildReportUtils.ps1
11
Keith Hill

最近同じ問題に遭遇しました。次の記事は、問題の解決に役立ちました。 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
6
Skyler

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

$MyInvocation.MyCommand.Path

それが役に立てば幸い !

セドリック

5
Cédric Rup

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

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

1
EBGreen

ありがとう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ディレクトリをここにあるパスのいずれかにコピーします$ env:PSModulePath

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

CommandType     Name                                               ModuleName                                                                                                                                                
-----------     ----                                               ----------                                                                                                                                                
Function        Get-ScriptDirectory                                MyModule  
0
Coding101