When writing portable scripts, one of the first challenges is locating files relative to the script itself. Whether in PowerShell or a classic Batch file, I need a reliable way to find my script’s “home” directory. This guide breaks down the two most important tools I use for this job: $PSScriptRoot for PowerShell and %~dp0 for Batch.


1. The Batch Method

  • cd /d %~dp0

In Windows Batch scripting (.bat, .cmd), the magic variable %~dp0 is the standard for getting the script’s directory. I often use it with cd to change the working directory.

The Command

cd /d %~dp0

This command tells the script to change its current working directory to the folder where the script file itself is located.

Deep Dive: What is %~dp0?

This cryptic variable is a combination of modifiers for the %0 argument, which represents the script file itself.

Part Name Description Example (D:\Tools\MyScript.bat)
%...0 Argument 0 Represents the batch file being executed. D:\Tools\MyScript.bat
~ Modifier Expands %0 and removes any surrounding quotes. D:\Tools\MyScript.bat
d Drive Extracts the drive letter from the path. D:
p Path Extracts the directory path from the path. \Tools\

When I combine them, %~dp0 expands to the drive and path of the script, including a trailing backslash.

Example Scenario:

  • My script is at D:\Tools\MyScript.bat.
  • I am currently in C:\Users\Admin.
  • I run D:\Tools\MyScript.bat.

Inside the script, the command cd /d %~dp0 does the following:

  1. %~dp0 expands to D:\Tools\.
  2. The /d switch allows cd to change the drive in addition to the directory.
  3. The working directory is now D:\Tools\.

Why is this important?

  • Portability: My script can be moved anywhere and will still find its files.
  • Reliability: It avoids errors from assuming the user is running the script from a specific directory.
  • Relative Paths: I can reliably reference other files in the same folder, like config.txt or .\data\settings.json.

2. The PowerShell Method: $PSScriptRoot

PowerShell introduced $PSScriptRoot, a modern, more readable, and safer automatic variable to accomplish the same goal.

The Command

Set-Location -LiteralPath $PSScriptRoot

This command changes the current working directory to the folder containing the running PowerShell script.

Deep Dive: What is $PSScriptRoot?

$PSScriptRoot is an automatic variable populated by the PowerShell engine when a script file is executed.

Property Description
Type System.String
Value The absolute path of the folder where the script is located.
Availability PowerShell 3.0 and later.
Scope Only available within the script file being executed. It is $null in an interactive console session.

When Does It Work?

Context $PSScriptRoot Value
Script file (.ps1) The script’s parent folder path.
Script module (.psm1) The module’s parent folder path.
Dot-sourced script The path of the script being dot-sourced.
Interactive Console $null (it has no value).

Benefits of $PSScriptRoot

  • Readability: The name is self-explanatory, unlike %~dp0.
  • Security: Using it with -LiteralPath in commands like Set-Location prevents issues with folder names that contain special characters (like [ or ]).
  • Native Integration: It’s a first-class citizen in PowerShell. Use it with Join-Path for robustly building file paths.

Common Use Cases

1. Changing to the script’s directory:

Set-Location -LiteralPath $PSScriptRoot

2. Loading a relative file securely:

$configPath = Join-Path -Path $PSScriptRoot -ChildPath 'config.json'
$config = Get-Content $configPath | ConvertFrom-Json

3. Dot-sourcing a helper script:

. (Join-Path $PSScriptRoot '.\lib\HelperFunctions.ps1')

Legacy Support (PowerShell 2.0)

If I need to support PowerShell 2.0 (which is rare today), $PSScriptRoot is not available. I can define it manually at the top of my script:

if ($PSVersionTable.PSVersion.Major -lt 3) {
    $PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
}


🧠 Quick Reference: Batch vs. PowerShell

Purpose Batch (.bat) PowerShell (.ps1)
Get Script Folder %~dp0 $PSScriptRoot
Change to Script Folder cd /d %~dp0 Set-Location $PSScriptRoot
Build Relative Path %~dp0data\file.txt Join-Path $PSScriptRoot 'data\file.txt'
Readability Low (Cryptic) High (Self-explanatory)
Security Lower Higher (with -LiteralPath)

🏁 Conclusion

Both %~dp0 and $PSScriptRoot are fundamental tools for creating reliable, portable scripts.

  • In Batch, I always use cd /d %~dp0 at the start of my script.
  • In PowerShell, I use $PSScriptRoot with Set-Location and Join-Path to ensure my script can always find its way home.

Adopting these patterns makes my automation more robust and easier to maintain.