To complement Abraham Zinala's helpful answer with some background information:
From a usability perspective, $d | Remove-Item
and Remove-Item $d
should work the same; the reasons they don't are (written as of PowerShell Core 7.2.3):
Argument-based parameter binding is, unfortunately, less sophisticated than pipeline-based binding:
Notably, the ValueFromPipelineByPropertyName
property on the -LiteralPath
parameter, whose alias is -PSPath
, is only honored for input objects provided via the pipeline ($d | Remove-Item
) and not also when provided as an argument (Remove-Item $d
).
With pipeline input, it is the .PSPath
property that PowerShell's provider cmdlets decorate their output objects with that robustly binds to -LiteralPath
, because the .PSPath
property value is a full, provider-qualified path - see this answer for a detailed explanation.
This binding asymmetry is the subject of GitHub issue #6057.
If you pass a file- or directory-info object as an argument and you do so positionally (i.e. without preceding the argument with the target-parameter name):
It is the wildcard-based -Path
parameter that is targeted:
This is normally not a problem, but does become one if the intended-as-literal path happens to contain [
characters (e.g., c:\foo\file[1].txt
), given that [
is a metacharacter in PowerShell's wildcard language.
Using a named argument that explicitly targets the -LiteralPath
parameter avoids this problem (note that PowerShell (Core) 7+ allows you to shorten to -lp
):
# Note: Still not robust in *Windows PowerShell* - see below.
Remove-Item -LiteralPath $d
Both -Path
and -LiteralPath
are [string[]]
-typed, i.e. they accept an array of strings, which in argument-based parameter binding means that any non-string argument is simply stringified (loosely speaking, like calling .ToString()
on it)[1]:
In Windows PowerShell - as you've experienced - this can lead to subtle but potentially destructive bugs - because System.IO.FileInfo
and System.IO.DirectoryInfo
instances situationally stringify to only their .Name
property, not to their .FullName
property - see this answer for details.
In PowerShell (Core) 7+ this is no longer a problem - stringification now consistently uses .FullName
.
However, stringification can still lead to failure for items returned by a PowerShell provider other than the file-system one, such as the registry provider:
# !! The 2nd command fails, because $regItem stringifies to
# !! "HKEY_CURRENT_USER\Console" (the registry-native path),
# !! which Get-Item then misinterprets as a relative file-system path.
$regItem = Get-Item HKCU:\Console
Get-Item $regItem
The upshot:
For target cmdlets that have -Path
/ -LiteralPath
parameters, provide arguments that are provider items, such as output by Get-ChildItem
and Get-Item
:
preferably via the pipeline:
# Robust, in both PowerShell editions.
Get-ChildItem C:\temp\junk -file | Remove-Item
If you must use an argument, target -LiteralPath
explicitly, and use the .PSPath
property:
# Robust, in both PowerShell editions.
$d = Get-ChildItem C:\temp\junk -file
Remove-Item -LiteralPath $d.PSPath
If, by contrast, you need to pass the paths of provider items to .NET APIs or external programs:
Since .PSPath
values contain a PowerShell provider prefix (e.g., Microsoft.PowerShell.Core\FileSystem::
) in front of the item's native path, such values aren't directly understood by outside parties.
The robust approach is to pipe a provider item to Convert-Path
in order to convert it to a native path in isolation (piping again results in robust .PSPath
binding to -LiteralPath
):
$d | Convert-Path # -> 'C:\temp\junk\delete-me'
This also works with path strings that are based on PowerShell-only drives, e.g:
Convert-Path -LiteralPath HKCU:\Console # -> 'HKEY_CURRENT_USER\Console'
[1] For most .NET types, PowerShell indeed calls .ToString()
, but requests culture-invariant formatting, where supported, via the IFormattable
interface; for select types, notably arrays and [pscustomobject]
, it uses custom stringification in lieu of calling .ToString()
- see this answer for details.