relative path in Import-Module - powershell

I have directory structure that looks like this:
C:\TFS\MasterScript\Script1.ps1
C:\TFS\ChildScript\Script2.ps1
What i want to do is specify the relative path in Script2.ps1 to look for Script1.ps1 in the directory hirearchy.
This is what i tried in Script2.ps1:
Import-Module ../MasterScript/Script1.ps1
but it does not work and says it cannot find the module.
If i say Import-Module C:\TFS\MasterScript\Script1.ps1, it works fine.
What am i missing here?

When you use a relative path, it is based off the currently location (obtained via Get-Location) and not the location of the script. Try this instead:
$ScriptDir = Split-Path -parent $MyInvocation.MyCommand.Path
Import-Module $ScriptDir\..\MasterScript\Script.ps1
In PowerShell v3, you can use the automatic variable $PSScriptRoot in scripts to simplify this to:
# PowerShell v3 or higher
#requires -Version 3.0
Import-Module $PSScriptRoot\..\MasterScript\Script.ps1

The new Method for this is $PSScriptRoot
Import-Module $PSScriptRoot\Script1.ps1
Nice little one liner.

This worked for me:
$selfPath = (Get-Item -Path "." -Verbose).FullName
$dllRelativePath = "........"
$dllAbsolutePath = Join-Path $selfPath $dllRelativePath
Import-Module $dllAbsolutePath

Related

$PSScriptRoot returns AppData path instead of the running script's path

So I'm trying something out and doing comparisons. Everything else works fine but when I use Ps1 to Exe by F2KO to get the running script's path, it returns something like this
C:\Users\justMe\AppData\Local\Temp\447F.tmp\4480.tmp\Test.ps1
I already tried using the code below but it returns the same AppData path.
$ScriptPath = (Split-Path -parent $MyInvocation.MyCommand.Definition) + "\Test.ps1"
Write-Host "Path: " $ScriptPath
Can anyone help out on this? Thanks.

Install an .MSI file from the same directory as the powershell script without setting a static file path

I have a script that we use that is in Powershell however I need the script to be able to find the files that it needs to install an application dynamically as users can copy the folder to any folder on their computer. Currently I have the below code set, I guess my question is, if the script is in the same folder as the install files. How do I tell powershell to just look in the directory that its being ran from for the install files?
$Install = "C:\Other Folder\File.msi"
$InstallArg = "/quite /norestart"
Start-Process '
-FilePath $Install '
-ArgumentList $InstallArg '
-PassThru | Wait-Process
Any help would be appreciated. Thank you.
Update, I found that I have to be in the directory the script is in. However since we have to run ISE with admin credentials it automatically defaults to C:\Windows\System32 as the directory powershell is looking in regardless if I tell it to open the script. If that is the case how can I tell it to look where the script is located so that it can find the files that it needs?
I have found my answer below is how I got it to work with our current situation. Thank you Thomas for the help!
$ScriptLocation = Get-ChildItem -Path C:\Users -Filter Untitled2.ps1 -Recurse | Select Directory
cd $ScriptLocation.Directory
$Install = ".\Install.msi"
$InstallArg = "/quite /norestart"
Start-Process '
-FilePath $Install '
-ArgumentList $InstallArg '
-PassThru | Wait-Process
Define a relative path:
$Install = ".\File.msi"
If you are not running the script inside the directory, where the script itself and the executable are stored, you will have to determine the absolute path to it. With PowerShell 3+ you can easily determine the directory, where your script is stored, using the $PSScriptRoot variable. You could define your $Install variable like this:
$Install = Join-Path -Path $PSScriptRoot -ChildPath "File.msi"
Thank you so much for your help everyone! I accidentally stumbled upon the perfect solution.
# 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

Using PSScriptRoot with Import-CSV

As detailed in:
PowerShell-Import-Local-CSV
$empCsv = Import-Csv "$PSScriptRoot\addEmp.csv"
Is classed as an answer. This doesn't work for me when i run this code inside a function. How can i test this?
You can test your Powershell version with $PSVersionTable.PSVersion.
If it is below version 3.0 you can use:
Split-Path $script:MyInvocation.MyCommand.Path -Parent
to find the directory the current script is running from instead of $PSScriptRoot.
$PSScriptRoot will provide the path for the currently running script/function, so if a script runs a function from another location, $PSScriptRoot will contain that other location.
If you want to get the path of the script that's calling the function, leverage $MyInvocation:
$empCsv = Import-Csv "$(Split-Path $MyInvocation.ScriptName)\addEmp.csv"
Demo
Create the Test.psm1 file.
Create the Caller.ps1 file in a subfolder.
Run Caller.ps1.
$PSScriptRoot will refer to Test.psm1 location, $MyInvocation will refer to Caller.ps1 location.
Test.psm1:
Function Test{
Write-host "PSScriptRoot: $PSScriptRoot"
Write-host "MyInvocation: $(Split-Path $MyInvocation.ScriptName)"
}
Caller.ps1:
Import-Module ..\Test.psm1 -Force
Test

Difference between $MyInvocation and $PSScriptRoot [duplicate]

Whenever I need to reference a common module or script, I like to use paths relative to the current script file. That way, my script can always find other scripts in the library.
So, what is the best, standard way of determining the directory of the current script? Currently, I'm doing:
$MyDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)
I know in modules (.psm1) you can use $PSScriptRoot to get this information, but that doesn't get set in regular scripts (i.e. .ps1 files).
What's the canonical way to get the current PowerShell script file's location?
PowerShell 3+
# This is an automatic variable set to the current file's/module's directory
$PSScriptRoot
PowerShell 2
Prior to PowerShell 3, there was not a better way than querying the
MyInvocation.MyCommand.Definition property for general scripts. I had the following line at the top of essentially every PowerShell script I had:
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
If you are creating a V2 Module, you can use an automatic variable called
$PSScriptRoot.
From PS > Help automatic_variable
$PSScriptRoot
Contains the directory from which the script module is being executed.
This variable allows scripts to use the module path to access other
resources.
For PowerShell 3.0
$PSCommandPath
Contains the full path and file name of the script that is being run.
This variable is valid in all scripts.
The function is then:
function Get-ScriptDirectory {
Split-Path -Parent $PSCommandPath
}
For PowerShell 3+
function Get-ScriptDirectory {
if ($psise) {
Split-Path $psise.CurrentFile.FullPath
}
else {
$global:PSScriptRoot
}
}
I've placed this function in my profile. It works in ISE using F8/Run Selection too.
Maybe I'm missing something here... but if you want the present working directory you can just use this: (Get-Location).Path for a string, or Get-Location for an object.
Unless you're referring to something like this, which I understand after reading the question again.
function Get-Script-Directory
{
$scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
return Split-Path $scriptInvocation.MyCommand.Path
}
I use the automatic variable $ExecutionContext.
It will work from PowerShell 2 and later.
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath('.\')
$ExecutionContext
Contains an EngineIntrinsics object that represents the
execution context of the Windows PowerShell host. You can
use this variable to find the execution objects that are
available to cmdlets.
Very similar to already posted answers, but piping seems more PowerShell-like:
$PSCommandPath | Split-Path -Parent
It took me a while to develop something that took the accepted answer and turned it into a robust function.
I am not sure about others, but I work in an environment with machines on both PowerShell version 2 and 3, so I needed to handle both. The following function offers a graceful fallback:
Function Get-PSScriptRoot
{
$ScriptRoot = ""
Try
{
$ScriptRoot = Get-Variable -Name PSScriptRoot -ValueOnly -ErrorAction Stop
}
Catch
{
$ScriptRoot = Split-Path $script:MyInvocation.MyCommand.Path
}
Write-Output $ScriptRoot
}
It also means that the function refers to the Script scope rather than the parent's scope as outlined by Michael Sorens in one of his blog posts.
Using pieces from all of these answers and the comments, I put this together for anyone who sees this question in the future. It covers all of the situations listed in the other answers, and I've added another one I found as a fail-safe.
function Get-ScriptPath()
{
# If using PowerShell ISE
if ($psISE)
{
$ScriptPath = Split-Path -Parent -Path $psISE.CurrentFile.FullPath
}
# If using PowerShell 3.0 or greater
elseif($PSVersionTable.PSVersion.Major -gt 3)
{
$ScriptPath = $PSScriptRoot
}
# If using PowerShell 2.0 or lower
else
{
$ScriptPath = split-path -parent $MyInvocation.MyCommand.Path
}
# If still not found
# I found this can happen if running an exe created using PS2EXE module
if(-not $ScriptPath) {
$ScriptPath = [System.AppDomain]::CurrentDomain.BaseDirectory.TrimEnd('\')
}
# Return result
return $ScriptPath
}
I needed to know the script name and where it is executing from.
Prefixing "$global:" to the MyInvocation structure returns the full path and script name when called from both the main script, and the main line of an imported .PSM1 library file. It also works from within a function in an imported library.
After much fiddling around, I settled on using $global:MyInvocation.InvocationName.
It works reliably with CMD launch, Run With Powershell, and the ISE.
Both local and UNC launches return the correct path.
I always use this little snippet which works for PowerShell and ISE the same way :
# 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
I found that the older solutions posted here didn't work for me on PowerShell V5. I came up with this:
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"
You might also consider split-path -parent $psISE.CurrentFile.Fullpath if any of the other methods fail. In particular, if you run a file to load a bunch of functions and then execute those functions with-in the ISE shell (or if you run-selected), it seems the Get-Script-Directory function as above doesn't work.
If you want to load modules from a path relative to where the script runs, such as from a "lib" subfolder", you need to use one of the following:
$PSScriptRoot which works when invoked as a script, such as via the PowerShell command
$psISE.CurrentFile.FullPath which works when you're running inside ISE
But if you're in neither, and just typing away within a PowerShell shell, you can use:
pwd.Path
You can could assign one of the three to a variable called $base depending on the environment you're running under, like so:
$base=$(if ($psISE) {Split-Path -Path $psISE.CurrentFile.FullPath} else {$(if ($global:PSScriptRoot.Length -gt 0) {$global:PSScriptRoot} else {$global:pwd.Path})})
Then in your scripts, you can use it like so:
Import-Module $base\lib\someConstants.psm1
Import-Module $base\lib\myCoolPsModule1.psm1
#etc.
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

What's the best way to determine the location of the current PowerShell script?

Whenever I need to reference a common module or script, I like to use paths relative to the current script file. That way, my script can always find other scripts in the library.
So, what is the best, standard way of determining the directory of the current script? Currently, I'm doing:
$MyDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)
I know in modules (.psm1) you can use $PSScriptRoot to get this information, but that doesn't get set in regular scripts (i.e. .ps1 files).
What's the canonical way to get the current PowerShell script file's location?
PowerShell 3+
# This is an automatic variable set to the current file's/module's directory
$PSScriptRoot
PowerShell 2
Prior to PowerShell 3, there was not a better way than querying the
MyInvocation.MyCommand.Definition property for general scripts. I had the following line at the top of essentially every PowerShell script I had:
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
If you are creating a V2 Module, you can use an automatic variable called
$PSScriptRoot.
From PS > Help automatic_variable
$PSScriptRoot
Contains the directory from which the script module is being executed.
This variable allows scripts to use the module path to access other
resources.
For PowerShell 3.0
$PSCommandPath
Contains the full path and file name of the script that is being run.
This variable is valid in all scripts.
The function is then:
function Get-ScriptDirectory {
Split-Path -Parent $PSCommandPath
}
For PowerShell 3+
function Get-ScriptDirectory {
if ($psise) {
Split-Path $psise.CurrentFile.FullPath
}
else {
$global:PSScriptRoot
}
}
I've placed this function in my profile. It works in ISE using F8/Run Selection too.
Maybe I'm missing something here... but if you want the present working directory you can just use this: (Get-Location).Path for a string, or Get-Location for an object.
Unless you're referring to something like this, which I understand after reading the question again.
function Get-Script-Directory
{
$scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
return Split-Path $scriptInvocation.MyCommand.Path
}
I use the automatic variable $ExecutionContext.
It will work from PowerShell 2 and later.
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath('.\')
$ExecutionContext
Contains an EngineIntrinsics object that represents the
execution context of the Windows PowerShell host. You can
use this variable to find the execution objects that are
available to cmdlets.
Very similar to already posted answers, but piping seems more PowerShell-like:
$PSCommandPath | Split-Path -Parent
It took me a while to develop something that took the accepted answer and turned it into a robust function.
I am not sure about others, but I work in an environment with machines on both PowerShell version 2 and 3, so I needed to handle both. The following function offers a graceful fallback:
Function Get-PSScriptRoot
{
$ScriptRoot = ""
Try
{
$ScriptRoot = Get-Variable -Name PSScriptRoot -ValueOnly -ErrorAction Stop
}
Catch
{
$ScriptRoot = Split-Path $script:MyInvocation.MyCommand.Path
}
Write-Output $ScriptRoot
}
It also means that the function refers to the Script scope rather than the parent's scope as outlined by Michael Sorens in one of his blog posts.
Using pieces from all of these answers and the comments, I put this together for anyone who sees this question in the future. It covers all of the situations listed in the other answers, and I've added another one I found as a fail-safe.
function Get-ScriptPath()
{
# If using PowerShell ISE
if ($psISE)
{
$ScriptPath = Split-Path -Parent -Path $psISE.CurrentFile.FullPath
}
# If using PowerShell 3.0 or greater
elseif($PSVersionTable.PSVersion.Major -gt 3)
{
$ScriptPath = $PSScriptRoot
}
# If using PowerShell 2.0 or lower
else
{
$ScriptPath = split-path -parent $MyInvocation.MyCommand.Path
}
# If still not found
# I found this can happen if running an exe created using PS2EXE module
if(-not $ScriptPath) {
$ScriptPath = [System.AppDomain]::CurrentDomain.BaseDirectory.TrimEnd('\')
}
# Return result
return $ScriptPath
}
I needed to know the script name and where it is executing from.
Prefixing "$global:" to the MyInvocation structure returns the full path and script name when called from both the main script, and the main line of an imported .PSM1 library file. It also works from within a function in an imported library.
After much fiddling around, I settled on using $global:MyInvocation.InvocationName.
It works reliably with CMD launch, Run With Powershell, and the ISE.
Both local and UNC launches return the correct path.
I always use this little snippet which works for PowerShell and ISE the same way :
# 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
I found that the older solutions posted here didn't work for me on PowerShell V5. I came up with this:
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"
You might also consider split-path -parent $psISE.CurrentFile.Fullpath if any of the other methods fail. In particular, if you run a file to load a bunch of functions and then execute those functions with-in the ISE shell (or if you run-selected), it seems the Get-Script-Directory function as above doesn't work.
If you want to load modules from a path relative to where the script runs, such as from a "lib" subfolder", you need to use one of the following:
$PSScriptRoot which works when invoked as a script, such as via the PowerShell command
$psISE.CurrentFile.FullPath which works when you're running inside ISE
But if you're in neither, and just typing away within a PowerShell shell, you can use:
pwd.Path
You can could assign one of the three to a variable called $base depending on the environment you're running under, like so:
$base=$(if ($psISE) {Split-Path -Path $psISE.CurrentFile.FullPath} else {$(if ($global:PSScriptRoot.Length -gt 0) {$global:PSScriptRoot} else {$global:pwd.Path})})
Then in your scripts, you can use it like so:
Import-Module $base\lib\someConstants.psm1
Import-Module $base\lib\myCoolPsModule1.psm1
#etc.
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