Functional differences between $PSScriptRoot and $MyInvocation
Asked Answered
G

3

8

Problem

I am working with Jenkins to deploy PowerShell scripts remotely. As such, I am trying to figure out if there will be problems utilizing $PSScriptRoot over $MyInvocation.MyCommand.Path for getting the current scripts root directory.

Details

A colleague in passing told me that utilizing $PSScriptRoot would be a bad idea for remote functionality as I might occasionally find that it does not return the expected value at runtime for one reason or another, even if it worked previously. However, could not explain why that would be.

In my research, I have not found anything on this that could explain this further or what the best practice way for avoiding such a problem would be. I primarily found that these two are basically interchangeable; however, $PSScriptRoot can only be used in PowerShell v3 or later. And through the help of several of you already, I have also come to learn that $MyInvocation has situational differences that allows it to change based on scope and module. But I still haven't found out if or why this would be a problem with PowerShell Remoting.

Example 001
So I have a PowerShell script in Jenkins, that uses $PSScriptRoot to act as the means of finding a script I wish to call via a relative path. Would I be able to rely on this to always provide the same/expected path to said script?

Example 002
Using a PowerShell script that was called by a PowerShell script that Jenkins initiated, would I be able to expect $PSScriptRoot to be able to provide me with the path of where that script actually sits, or would it give me a path based on Jenkins?

Personally, I am expecting both to be that $PSScriptRoot will provide me with the actual physical location of the script that is running rather than a relative path that changes based on the initial script that called it.

Since having this understanding would help save me a lot of time and headache, I am hoping that fellow programmers such as yourself could help enlighten me to IF this is true, and why such a problem would happen.

The Question

I am trying to find out if using $PSScriptRoot would cause me problems in PowerShell Remoting that would make using $MyInvocation a more viable option?

Gid answered 10/8, 2017 at 13:32 Comment(7)
The differences between $MyInvocation.MyCommand.Path and $PSScriptRoot are that a) one includes the filename and the other does not, and b) one works on all PowerShell versions and the other only on PowerShell v3 and above. Period.Griffy
You know... you don't need to be a jerk as you are trying to "help" someone. I am asking this as someone still somewhat new to PowerShell. I am merely asking as I am presently working on a complex problem that I am told would be bad to use $PSScriptRoot for because I would not always get the route I am expecting it to provide.Gid
@Gid again, as I said in my now deleted comments, you need to post specific questions here. What is the scenario you're describing?Emmy
@Emmy edited once again to an even more specific questionGid
@Gid 1) have you tried it out to see what it returns? 2) What do you think the current script is in that context?Emmy
@Gid also see my answer, where I addressed what will show up when you use it in Jenkins. It's up to you if what it returns will be a problem, as I'm not sure which file you're trying to get the path of.Emmy
@Emmy 1) I have tried it and so far it works as I would expect it to 2) I think the current context would be where it's physical path is. So if I am calling it via a relative path, by using $PSScriptRoot, I am expecting that the full path would be provided by this automatic variable based on the relative path it was called from. EXAMPLE: If Jenkins calls the script, I expect the $PSScriptRoot to be a spot on Jenkins' remote repository.Gid
A
8
$PSScriptRoot.GetType().FullName
> System.String
$PSScriptRoot
> C:\Temp

$PSScriptRoot is an automatic variable which only holds a string object of the current script's directory.

$MyInvocation.GetType().FullName
> System.Management.Automation.InvocationInfo
$MyInvocation
> MyCommand             : test.ps1
> BoundParameters       : {}
> UnboundArguments      : {}
> ScriptLineNumber      : 0
> OffsetInLine          : 0
> HistoryId             : 4
> ScriptName            : 
> Line                  : 
> PositionMessage       : 
> PSScriptRoot          : 
> PSCommandPath         : 
> InvocationName        : C:\Temp\test.ps1
> PipelineLength        : 2
> PipelinePosition      : 1
> ExpectingInput        : False
> CommandOrigin         : Internal
> DisplayScriptPosition : 

$MyInvocation | Get-Member -Force
   TypeName: System.Management.Automation.InvocationInfo

Name                      MemberType   Definition                                                             
----                      ----------   ----------                                                             
pstypenames               CodeProperty System.Collections.ObjectModel.Collection`1[[System.String, mscorlib...
psadapted                 MemberSet    psadapted {MyCommand, BoundParameters, UnboundArguments, ScriptLineN...
psbase                    MemberSet    psbase {MyCommand, BoundParameters, UnboundArguments, ScriptLineNumb...
psextended                MemberSet    psextended {}                                                          
psobject                  MemberSet    psobject {BaseObject, Members, Properties, Methods, ImmediateBaseObj...
Equals                    Method       bool Equals(System.Object obj)                                         
GetHashCode               Method       int GetHashCode()                                                      
GetType                   Method       type GetType()                                                         
get_BoundParameters       Method       System.Collections.Generic.Dictionary[string,System.Object] get_Boun...
get_CommandOrigin         Method       System.Management.Automation.CommandOrigin get_CommandOrigin()         
get_DisplayScriptPosition Method       System.Management.Automation.Language.IScriptExtent get_DisplayScrip...
get_ExpectingInput        Method       bool get_ExpectingInput()                                              
get_HistoryId             Method       long get_HistoryId()                                                   
get_InvocationName        Method       string get_InvocationName()                                            
get_Line                  Method       string get_Line()                                                      
get_MyCommand             Method       System.Management.Automation.CommandInfo get_MyCommand()               
get_OffsetInLine          Method       int get_OffsetInLine()                                                 
get_PipelineLength        Method       int get_PipelineLength()                                               
get_PipelinePosition      Method       int get_PipelinePosition()                                             
get_PositionMessage       Method       string get_PositionMessage()                                           
get_PSCommandPath         Method       string get_PSCommandPath()                                             
get_PSScriptRoot          Method       string get_PSScriptRoot()                                              
get_ScriptLineNumber      Method       int get_ScriptLineNumber()                                             
get_ScriptName            Method       string get_ScriptName()                                                
get_UnboundArguments      Method       System.Collections.Generic.List[System.Object] get_UnboundArguments()  
set_DisplayScriptPosition Method       void set_DisplayScriptPosition(System.Management.Automation.Language...
ToString                  Method       string ToString()                                                      
BoundParameters           Property     System.Collections.Generic.Dictionary[string,System.Object] BoundPar...
CommandOrigin             Property     System.Management.Automation.CommandOrigin CommandOrigin {get;}        
DisplayScriptPosition     Property     System.Management.Automation.Language.IScriptExtent DisplayScriptPos...
ExpectingInput            Property     bool ExpectingInput {get;}                                             
HistoryId                 Property     long HistoryId {get;}                                                  
InvocationName            Property     string InvocationName {get;}                                           
Line                      Property     string Line {get;}                                                     
MyCommand                 Property     System.Management.Automation.CommandInfo MyCommand {get;}              
OffsetInLine              Property     int OffsetInLine {get;}                                                
PipelineLength            Property     int PipelineLength {get;}                                              
PipelinePosition          Property     int PipelinePosition {get;}                                            
PositionMessage           Property     string PositionMessage {get;}                                          
PSCommandPath             Property     string PSCommandPath {get;}                                            
PSScriptRoot              Property     string PSScriptRoot {get;}                                             
ScriptLineNumber          Property     int ScriptLineNumber {get;}                                            
ScriptName                Property     string ScriptName {get;}                                               
UnboundArguments          Property     System.Collections.Generic.List[System.Object] UnboundArguments {get;} 

Function Example { $MyInvocation } Example

MyCommand             : Example
BoundParameters       : {}
UnboundArguments      : {}
ScriptLineNumber      : 8
OffsetInLine          : 1
HistoryId             : 6
ScriptName            : C:\Temp\test.ps1
Line                  : Example
PositionMessage       : At C:\Temp\test.ps1:8 char:1
                        + Example
                        + ~~~~~~~
PSScriptRoot          : C:\Temp
PSCommandPath         : C:\Temp\test.ps1
InvocationName        : Example
PipelineLength        : 1
PipelinePosition      : 1
ExpectingInput        : False
CommandOrigin         : Internal
DisplayScriptPosition : 

$MyInvocation is an automatic variable with a very different type, generated for each scope. Its members and utility varies based on scope.

Tests completed on PSv5.1, Windows 7 SP1

Alvardo answered 10/8, 2017 at 14:9 Comment(1)
I see. I was not aware of how to bring up this information (still somewhat new to this programming language) and my "help" files for PS don't exist (system tells me to install them), but as a work PC, I don't have admin rights to do so. As such, this information is very helpful. Thank you once again.Gid
E
4

Remember that both of these variables refer to the file that is being executed. If there is no file, they are empty.

In remoting, like when you use Invoke-Command -ScriptBlock { } there is no file.

It might work if you are calling a function that's in a module in the remoting session, and that function uses $PSScriptRoot or $MyInvocaton... in which case it would likely return whatever file it's in.

In the case of something like Jenkins, that's not remoting. Jenkins creates a temporary file and runs your code out of it, so that's the file that will be returned.

Emmy answered 10/8, 2017 at 14:15 Comment(5)
@TheIncorrigible1 yes, I'm not sure how that contradicts what I said. Could you expand on your objection?Emmy
@TheIncorrigible1 So does $PSScriptRoot.Griffy
@TheIncorrigible1 again, what part of my answer is inaccurate to the point where is deserves a downvote?Emmy
@TheIncorrigible1 The OP didn't say anything about using this in the context of a module. In that particular situation you'd see a difference if (and only if) the variables are used without a scope modifier. Other than that the result is the same (sans the filename, of course).Griffy
@TheIncorrigible1 getting the current scripts root directory there is no current script if you're executing code that's not backed by a file.Emmy
D
0

Although (Split-Path -Parent ($MyInvocation.MyCommand.Path)) is the longer of the two command options that returns the path, as @'Maximilian Burszley' pointed out, $PSScriptRoot is a simple string which [while not tested prior to this post] may return an incorrect path if you make the call from within a module or child script not co-located in the same path as the main script.

The bigger pitfall (IMO) is calling either command from a child scope, like when you use Invoke-Command -ScriptBlock { }, as pointed out above.

Direful answered 25/10, 2023 at 16:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.