Using relative file paths for your PowerShell script’s supporting files ensure it continues to work when the script and files get moved around. Here’s how.
Unpredictable File Paths
My first intensive use of PowerShell was for scripting software installations/configurations in SCCM packages. It seemed like every situation called for something above and beyond a straightforward MSI install.
I would develop and test the SCCM package on my local system with a sandbox VM prior to deploying in SCCM. The SCCM client would then download that package to a cached location on each individual client. From development to deployment, I couldn’t guarantee the absolute file path of my script and any supporting files would be constant. I needed my script to be able to figure out where the supporting files were in relation to itself.
The newer way: $PSScriptRoot
$PSScriptRoot is an Automatic Variable, which are built-in variables that contain information about the PowerShell environment itself. $PSScriptRoot contains the directory path of the script being executed currently. Originally $PSScriptRoot was only applicable to script modules (.psm1), but beginning with PowerShell 3.0, it works for all PowerShell script files.
From the console if I type $PSScriptRoot and press ENTER, it returns nothing. $PSScriptRoot only gets a value when a script is executing, and then is destroyed when the script terminates.
PS C:\Users\aaron> $PSScriptRoot PS C:\Users\aaron>
Let’s say I have a script and config file sitting in C:\TEMP\DemoScript. I would concatenate the value of $PSScriptRoot with a backslash and the config file name:
# DemoScript.ps1 $configFilePath = $PSScriptRoot + "\DemoFile.cfg" Write-Host $configFilePath
If I run DemoScript.ps1, you see $PSScriptRoot gets a value and correctly builds the config file path:
PS C:\Users\aaron> C:\TEMP\DemoScript\DemoScript.ps1 C:\TEMP\DemoScript\DemoFile.cfg
Now let’s move the DemoScript directory to C:\TEMP\NewRootFolder and run DemoScript.ps1 again. The config file path is built correctly to accommodate the file move.
PS C:\Users\aaron> C:\TEMP\NewRootFolder\DemoScript\DemoScript.ps1 C:\TEMP\NewRootFolder\DemoScript\DemoFile.cfg
This requires all supporting files be in the same folder as the script or a child folder.
The old way: $MyInvocation.MyCommand.Path
I started on the SCCM packages prior to PowerShell 3.0, when $PSScriptRoot didn’t exist. Instead, we parsed the script path from another Automatic Variable, $MyInvocation.MyCommand.Path, and used it in the same fashion as $PSScriptRoot.
# DemoScript.ps1 $scriptPath = split-path -parent $MyInvocation.MyCommand.Path $configFilePath = $scriptPath + "\DemoFile.cfg" Write-Host $configFilePath
PS C:\Users\aaron> C:\TEMP\NewRootFolder\DemoScript\DemoScript.ps1 C:\TEMP\NewRootFolder\DemoScript\DemoFile.cfg
It works, but it requires an extra line of code and isn’t as simple as $PSScriptRoot, but it is an option on any legacy systems you may have that don’t have PowerShell 3.0.
Make your script resilient with $PSScriptRoot
Use $PSScriptRoot to build absolute file paths from relative ones to make your script and supporting files portable and resilient. As part of this, it is a good practice to create a new folder to become that common root when you start developing a new script that will have supporting files.
Reference
- technet.microsoft.com