In PowerShell, it’s quite common to be working with file paths when you’re reading from (or writing to) text files. For many automation scenarios involving PowerShell scripts, that means building / constructing a complete path to whatever file(s) your script is working with. There are several approaches to constructing file paths in a PowerShell script or CLI session, and I want to go over a couple here.
Using a Hard-Coded String
One of the most straightforward common approaches is to explicitly define the entire file path as a string:
|
|
However, relying on a fully hard-coded path is not a best practice, and has several drawbacks. First, notice that this example uses C: as the starting point, which locks your command or script to Windows. With PowerShell Core 6 and later, we’re cross-platform so we should consider how our scripts would behave on macOS or Linux. Presuming your scripts will only ever run on Windows immediately limits your scripts, and makes it harder to share them with others.
Another big drawback is the use of backslashes \ in the path. While backslashes are the standard directory separators on Windows, everything else typically uses forward slashes / . Every Unix-like systems out there (macOS and Linux distros) could run your fancy script if you take this into account. PowerShell often normalizes these separators internally, and Windows itself actually supports the forward slash in some scenarios, but this won’t always hold true. So for maximum portability, it’s a good habit to use / when manually building a folder or file path.
Moreover, if you need use a path like E:\pathtofile\mydata.txt on Windows or similar on other operating systems, consider using environment variables or built-in PowerShell variables. They offer the greatest flexibility, and in PS Core 6 and onward you get $IsWindows, $IsLinux, and $IsMacOS to tell your code which operating system it’s running in. On Windows, $env:SYSTEMDRIVE returns the system drive (often C:, but not necessarily). On Unix-like systems, you can default to root / . Using PS env variables allows your script to adapt to the operating system dynamically:
|
|
Enter: Join-Path
PowerShell provides the Join-Path cmdlet, which combines multiple path segments into one fully qualified path. This approach is way more robust because Join-Path automatically picks the appropriate path separator based on the environment.
For example, the following returns C:\myfolder on Windows:
|
|
Joining Multiple Paths
Sometimes you’ll need to combine more than two path segments. A simplistic way might be:
|
|
Or:
|
|
Or, getting really wild and convoluted:
|
|
Yuck! None of these options are particularly clean or easy-to-read.
So let’s leverage PS Core capabilities, where the Join-Path cmdlet offers a parameter called -AdditionalChildPaths. This parameter accepts an array of string values, allowing you to join multiple path segments in one go:
|
|
Cool, huh? This way easier when dealing with multiple paths that you need to join. These parameters also work in a positional manner, so you’re not required to specify parameter names if you prefer a more concise style. While skipping parameter names isn’t typically recommended in PowerShell’s style guidelines, it’s often considered acceptable for commonly used cmdlets like Join-Path.
And Now For Some PS & .NET Path-Joining Kung-Fu
One of PowerShell’s strengths is its integration with the .NET framework. Whenever you need more advanced capabilities with file i/o stuff, .NET comes in clutch for PowerShell scripting. Especially if you are needing to do some fancy path building in PS 5.
The method I’ve recently taken to using, for building complex file paths, is using the [System.IO.Path] class along with its Combine() method. By leveraging Combine(), you can feed it multiple path segments at once, eliminating the need for tedious string concatenation.
Using .NET for path building mirrors the behavior of PowerShell Core’s Join-Path and its -AdditionalChildPaths parameter, yet it’s still compatible with earlier versions of PowerShell! This method ensures that your script or module remains adaptable and widely portable.
|
|
See you along the path… 😉